Merge "mediawiki.helplink: Use a SVG+PNG icon"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Sat, 21 Mar 2015 17:50:39 +0000 (17:50 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Sat, 21 Mar 2015 17:50:39 +0000 (17:50 +0000)
141 files changed:
Gruntfile.js
RELEASE-NOTES-1.25
autoload.php
composer.json
docs/sitescache.txt [new file with mode: 0644]
includes/DefaultSettings.php
includes/Defines.php
includes/EditPage.php
includes/GlobalFunctions.php
includes/Html.php
includes/TemplateParser.php
includes/api/i18n/fr.json
includes/api/i18n/gl.json
includes/api/i18n/it.json
includes/api/i18n/ksh.json
includes/api/i18n/zh-hans.json
includes/api/i18n/zh-hant.json
includes/debug/MWDebug.php
includes/debug/logger/legacy/Logger.php
includes/filerepo/file/LocalFile.php
includes/installer/i18n/hi.json
includes/installer/i18n/zh-hant.json
includes/jobqueue/JobQueueFederated.php
includes/media/ImageHandler.php
includes/media/MediaHandler.php
includes/page/ImagePage.php
includes/site/CachingSiteStore.php [new file with mode: 0644]
includes/site/DBSiteStore.php [new file with mode: 0644]
includes/site/FileBasedSiteLookup.php [new file with mode: 0644]
includes/site/HashSiteStore.php
includes/site/SiteListFileCache.php [deleted file]
includes/site/SiteListFileCacheBuilder.php [deleted file]
includes/site/SiteLookup.php [new file with mode: 0644]
includes/site/SiteSQLStore.php
includes/site/SiteStore.php
includes/site/SitesCacheFileBuilder.php [new file with mode: 0644]
includes/specialpage/FormSpecialPage.php
includes/specials/SpecialTags.php
languages/i18n/arq.json
languages/i18n/awa.json
languages/i18n/bar.json
languages/i18n/be-tarask.json
languages/i18n/bgn.json
languages/i18n/bho.json
languages/i18n/ca.json
languages/i18n/ce.json
languages/i18n/cs.json
languages/i18n/da.json
languages/i18n/de.json
languages/i18n/egl.json
languages/i18n/en.json
languages/i18n/es.json
languages/i18n/et.json
languages/i18n/fi.json
languages/i18n/fr.json
languages/i18n/frr.json
languages/i18n/gl.json
languages/i18n/he.json
languages/i18n/hi.json
languages/i18n/hu.json
languages/i18n/ia.json
languages/i18n/is.json
languages/i18n/it.json
languages/i18n/ka.json
languages/i18n/kk-cyrl.json
languages/i18n/ksh.json
languages/i18n/lt.json
languages/i18n/lzh.json
languages/i18n/mk.json
languages/i18n/ml.json
languages/i18n/mr.json
languages/i18n/nap.json
languages/i18n/ne.json
languages/i18n/pl.json
languages/i18n/pms.json
languages/i18n/qqq.json
languages/i18n/roa-tara.json
languages/i18n/sl.json
languages/i18n/sr-ec.json
languages/i18n/sr-el.json
languages/i18n/sv.json
languages/i18n/tcy.json
languages/i18n/tt-cyrl.json
languages/i18n/zh-hans.json
languages/i18n/zh-hant.json
languages/messages/MessagesDe.php
maintenance/rebuildSitesCache.php
package.json
resources/Resources.php
resources/lib/oojs-ui/i18n/is.json
resources/lib/oojs-ui/i18n/kk-cyrl.json
resources/lib/oojs-ui/i18n/krc.json
resources/lib/oojs-ui/i18n/ksh.json
resources/lib/oojs-ui/i18n/tr.json
resources/lib/oojs-ui/oojs-ui-mediawiki-icons-alerts.css
resources/lib/oojs-ui/oojs-ui-mediawiki-icons-content.css
resources/lib/oojs-ui/oojs-ui-mediawiki-icons-editing-advanced.css
resources/lib/oojs-ui/oojs-ui-mediawiki-icons-editing-core.css
resources/lib/oojs-ui/oojs-ui-mediawiki-icons-editing-list.css
resources/lib/oojs-ui/oojs-ui-mediawiki-icons-editing-styling.css
resources/lib/oojs-ui/oojs-ui-mediawiki-icons-interactions.css
resources/lib/oojs-ui/oojs-ui-mediawiki-icons-layout.css
resources/lib/oojs-ui/oojs-ui-mediawiki-icons-location.css
resources/lib/oojs-ui/oojs-ui-mediawiki-icons-media.css
resources/lib/oojs-ui/oojs-ui-mediawiki-icons-moderation.css
resources/lib/oojs-ui/oojs-ui-mediawiki-icons-movement.css
resources/lib/oojs-ui/oojs-ui-mediawiki-icons-user.css
resources/lib/oojs-ui/oojs-ui-mediawiki-icons-wikimedia.css
resources/lib/oojs-ui/oojs-ui-mediawiki.css
resources/lib/oojs-ui/oojs-ui-mediawiki.js
resources/lib/oojs-ui/oojs-ui.js
resources/lib/oojs/oojs.jquery.js
resources/src/mediawiki.api/mediawiki.api.options.js [new file with mode: 0644]
resources/src/mediawiki.special/mediawiki.special.block.js
resources/src/mediawiki.special/mediawiki.special.changeslist.css
resources/src/mediawiki.ui/components/inputs.less
resources/src/mediawiki/mediawiki.filewarning.js [new file with mode: 0644]
resources/src/mediawiki/mediawiki.filewarning.less [new file with mode: 0644]
resources/src/mediawiki/mediawiki.util.js
tests/browser/features/step_definitions/create_account_steps.rb
tests/browser/features/step_definitions/create_and_follow_wiki_link_steps.rb
tests/browser/features/step_definitions/edit_page_steps.rb
tests/browser/features/step_definitions/file_steps.rb
tests/browser/features/step_definitions/login_steps.rb
tests/browser/features/step_definitions/main_page_links_steps.rb
tests/browser/features/step_definitions/preferences_appearance_steps.rb
tests/browser/features/step_definitions/preferences_editing_steps.rb
tests/browser/features/step_definitions/preferences_user_profile_steps.rb
tests/browser/features/step_definitions/view_history_steps.rb
tests/phpunit/includes/site/CachingSiteStoreTest.php [new file with mode: 0644]
tests/phpunit/includes/site/DBSiteStoreTest.php [new file with mode: 0644]
tests/phpunit/includes/site/FileBasedSiteLookupTest.php [new file with mode: 0644]
tests/phpunit/includes/site/SiteListFileCacheBuilderTest.php [deleted file]
tests/phpunit/includes/site/SiteListFileCacheTest.php [deleted file]
tests/phpunit/includes/site/SiteSQLStoreTest.php
tests/phpunit/includes/site/SitesCacheFileBuilderTest.php [new file with mode: 0644]
tests/phpunit/includes/site/TestSites.php
tests/phpunit/structure/AutoLoaderTest.php
tests/qunit/QUnitTestResources.php
tests/qunit/suites/resources/mediawiki.api/mediawiki.api.options.test.js [new file with mode: 0644]
tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js

index a292d0b..573db69 100644 (file)
@@ -76,7 +76,9 @@ module.exports = function ( grunt ) {
                                frameworks: [ 'qunit' ],
                                reporters: [ 'dots' ],
                                singleRun: true,
-                               autoWatch: false
+                               autoWatch: false,
+                               // Some tests in extensions don't yield for more than the default 10s (T89075)
+                               browserNoActivityTimeout: 60 * 1000
                        },
                        main: {
                                browsers: [ 'Chrome' ]
@@ -97,8 +99,21 @@ module.exports = function ( grunt ) {
                }
        } );
 
+       grunt.registerTask( 'assert-mw-env', function () {
+               if ( !process.env.MW_SERVER ) {
+                       grunt.log.error( 'Environment variable MW_SERVER must be set.\n' +
+                               'Set this like $wgServer, e.g. "http://localhost"'
+                       );
+               }
+               if ( !process.env.MW_SCRIPT_PATH ) {
+                       grunt.log.error( 'Environment variable MW_SCRIPT_PATH must be set.\n' +
+                               'Set this like $wgScriptPath, e.g. "/w"');
+               }
+               return !!( process.env.MW_SERVER && process.env.MW_SCRIPT_PATH );
+       } );
+
        grunt.registerTask( 'lint', ['jshint', 'jscs', 'jsonlint', 'banana'] );
-       grunt.registerTask( 'qunit', 'karma:main' );
+       grunt.registerTask( 'qunit', [ 'assert-mw-env', 'karma:main' ] );
 
        grunt.registerTask( 'test', ['lint'] );
        grunt.registerTask( 'default', 'test' );
index 88bfc15..00ce6b1 100644 (file)
@@ -115,6 +115,8 @@ production.
   interface, reachable via IContextSource::getStats().
 * Move the jQuery Client library from being mastered in MediaWiki as v0.1.0 to a
   proper, published library, which is now tagged as v1.0.0.
+* A new message (defaulting to blank), 'editnotice-notext', can be shown to users
+  when they are editing if no edit notices apply to the page being edited.
 
 ==== External libraries ====
 * MediaWiki now requires certain external libraries to be installed. In the past
index 6b83fcb..eacf431 100644 (file)
@@ -189,6 +189,7 @@ $wgAutoloadLocalClasses = array(
        'CacheHelper' => __DIR__ . '/includes/cache/CacheHelper.php',
        'CacheTime' => __DIR__ . '/includes/parser/CacheTime.php',
        'CachedAction' => __DIR__ . '/includes/actions/CachedAction.php',
+       'CachingSiteStore' => __DIR__ . '/includes/site/CachingSiteStore.php',
        'CapsCleanup' => __DIR__ . '/maintenance/cleanupCaps.php',
        'Category' => __DIR__ . '/includes/Category.php',
        'CategoryFinder' => __DIR__ . '/includes/CategoryFinder.php',
@@ -277,6 +278,7 @@ $wgAutoloadLocalClasses = array(
        'DBMasterPos' => __DIR__ . '/includes/db/DatabaseUtility.php',
        'DBObject' => __DIR__ . '/includes/db/DatabaseUtility.php',
        'DBQueryError' => __DIR__ . '/includes/db/DatabaseError.php',
+       'DBSiteStore' => __DIR__ . '/includes/site/DBSiteStore.php',
        'DBUnexpectedError' => __DIR__ . '/includes/db/DatabaseError.php',
        'DataUpdate' => __DIR__ . '/includes/deferred/DataUpdate.php',
        'DatabaseBase' => __DIR__ . '/includes/db/Database.php',
@@ -417,6 +419,7 @@ $wgAutoloadLocalClasses = array(
        'FileBackendStoreShardDirIterator' => __DIR__ . '/includes/filebackend/FileBackendStore.php',
        'FileBackendStoreShardFileIterator' => __DIR__ . '/includes/filebackend/FileBackendStore.php',
        'FileBackendStoreShardListIterator' => __DIR__ . '/includes/filebackend/FileBackendStore.php',
+       'FileBasedSiteLookup' => __DIR__ . '/includes/site/FileBasedSiteLookup.php',
        'FileCacheBase' => __DIR__ . '/includes/cache/FileCacheBase.php',
        'FileDeleteForm' => __DIR__ . '/includes/FileDeleteForm.php',
        'FileDependency' => __DIR__ . '/includes/cache/CacheDependency.php',
@@ -1061,14 +1064,14 @@ $wgAutoloadLocalClasses = array(
        'SiteExporter' => __DIR__ . '/includes/site/SiteExporter.php',
        'SiteImporter' => __DIR__ . '/includes/site/SiteImporter.php',
        'SiteList' => __DIR__ . '/includes/site/SiteList.php',
-       'SiteListFileCache' => __DIR__ . '/includes/site/SiteListFileCache.php',
-       'SiteListFileCacheBuilder' => __DIR__ . '/includes/site/SiteListFileCacheBuilder.php',
+       'SiteLookup' => __DIR__ . '/includes/site/SiteLookup.php',
        'SiteObject' => __DIR__ . '/includes/site/Site.php',
        'SiteSQLStore' => __DIR__ . '/includes/site/SiteSQLStore.php',
        'SiteStats' => __DIR__ . '/includes/SiteStats.php',
        'SiteStatsInit' => __DIR__ . '/includes/SiteStats.php',
        'SiteStatsUpdate' => __DIR__ . '/includes/deferred/SiteStatsUpdate.php',
        'SiteStore' => __DIR__ . '/includes/site/SiteStore.php',
+       'SitesCacheFileBuilder' => __DIR__ . '/includes/site/SitesCacheFileBuilder.php',
        'Skin' => __DIR__ . '/includes/skins/Skin.php',
        'SkinApi' => __DIR__ . '/includes/skins/SkinApi.php',
        'SkinApiTemplate' => __DIR__ . '/includes/skins/SkinApiTemplate.php',
index 8c0cfed..484f472 100644 (file)
@@ -20,7 +20,7 @@
                "ext-iconv": "*",
                "leafo/lessphp": "0.5.0",
                "liuggio/statsd-php-client": "1.0.12",
-               "oojs/oojs-ui": "0.9.1",
+               "oojs/oojs-ui": "0.9.3",
                "php": ">=5.3.3",
                "psr/log": "1.0.0",
                "wikimedia/cdb": "1.0.1",
diff --git a/docs/sitescache.txt b/docs/sitescache.txt
new file mode 100644 (file)
index 0000000..13bf371
--- /dev/null
@@ -0,0 +1,42 @@
+MediaWiki's SiteStore can be cached and stored in a flat file,
+in a json format. If the SiteStore is frequently accessed, the
+file cache may provide a performance benefit over a database
+store, even with memcached in front of it.
+
+Configuration:
+
+File-based caching can be enabled by setting $wgSitesCacheFile
+to the file path of the cache file.
+
+The file can then be generated with the rebuildSitesCache.php
+maintenance script.
+
+Format:
+
+In the sites cache file, sites are listed in a key-value
+map, with the key being the site's global id (e.g. "enwiki")
+and a key-value map as the value. The site list is wrapped
+with in a "sites" key.
+
+Example:
+
+"sites": {
+       "aawiktionary": {
+               "globalid": "aawiktionary",
+               "type": "mediawiki",
+               "group": "wiktionary",
+               "source": "local",
+               "language": "aa",
+               "localids": [],
+               "config": [],
+               "data": {
+                       "paths": {
+                               "file_path": "http:\/\/aa.wiktionary.org\/w\/$1",
+                               "page_path": "http:\/\/aa.wiktionary.org\/wiki\/$1"
+                       }
+               },
+               "forward": false,
+               "internalid": 2666,
+               "identifiers": []
+       }
+}
index 80af4ae..1df809e 100644 (file)
@@ -3805,7 +3805,7 @@ $wgInterwikiFallbackSite = 'wiki';
  */
 
 /**
- * Specify the file location for the SiteStore json cache file.
+ * Specify the file location for the Sites json cache file.
  */
 $wgSitesCacheFile = false;
 
@@ -5320,6 +5320,11 @@ $wgDebugLogGroups = array();
  * inject an MWLoggerSpi instance into MWLoggerFactory and bypass the use of
  * this configuration variable entirely.
  *
+ * @par To completely disable logging:
+ * @code
+ * $wgMWLoggerDefaultSpi = array( 'class' => 'MWLoggerNullSpi' );
+ * @endcode
+ *
  * @since 1.25
  * @var array $wgMWLoggerDefaultSpi
  * @see MwLogger
index 8456c5d..c9263da 100644 (file)
@@ -208,7 +208,6 @@ require_once __DIR__ . '/libs/normal/UtfNormalDefines.php';
 /**@{
  * Hook support constants
  */
-define( 'MW_SUPPORTS_EDITFILTERMERGED', 1 );
 define( 'MW_SUPPORTS_PARSERFIRSTCALLINIT', 1 );
 define( 'MW_SUPPORTS_LOCALISATIONCACHE', 1 );
 define( 'MW_SUPPORTS_CONTENTHANDLER', 1 );
@@ -245,11 +244,6 @@ define( 'SFH_NO_HASH', 1 );
 define( 'SFH_OBJECT_ARGS', 2 );
 /**@}*/
 
-/**
- * Flags for Parser::replaceLinkHolders
- */
-define( 'RLH_FOR_UPDATE', 1 );
-
 /**@{
  * Autopromote conditions (must be here and not in Autopromote.php, so that
  * they're loaded for DefaultSettings.php before AutoLoader.php)
index 5eb07d1..94ed903 100644 (file)
@@ -2555,7 +2555,19 @@ class EditPage {
                }
 
                // Add edit notices
-               $wgOut->addHTML( implode( "\n", $this->mTitle->getEditNotices( $this->oldid ) ) );
+               $editNotices = $this->mTitle->getEditNotices( $this->oldid );
+               if ( count( $editNotices ) ) {
+                       $wgOut->addHTML( implode( "\n", $editNotices ) );
+               } else {
+                       $msg = wfMessage( 'editnotice-notext' );
+                       if ( !$msg->isDisabled() ) {
+                               $wgOut->addHTML(
+                                       '<div class="mw-editnotice-notext">'
+                                       . $msg->parseAsBlock()
+                                       . '</div>'
+                               );
+                       }
+               }
 
                if ( $this->isConflict ) {
                        $wgOut->wrapWikiMsg( "<div class='mw-explainconflict'>\n$1\n</div>", 'explainconflict' );
index 9ae6cb8..47e7a15 100644 (file)
@@ -1030,12 +1030,7 @@ function wfMatchesDomainList( $url, $domains ) {
  * @since 1.25 support for additional context data
  *
  * @param string $text
- * @param string|bool $dest Destination of the message:
- *     - 'all': both to the log and HTML (debug toolbar or HTML comments)
- *     - 'log': only to the log and not in HTML
- *   For backward compatibility, it can also take a boolean:
- *     - true: same as 'all'
- *     - false: same as 'log'
+ * @param string|bool $dest Unused
  * @param array $context Additional logging context data
  */
 function wfDebug( $text, $dest = 'all', array $context = array() ) {
@@ -1046,13 +1041,6 @@ function wfDebug( $text, $dest = 'all', array $context = array() ) {
                return;
        }
 
-       // Turn $dest into a string if it's a boolean (for b/c)
-       if ( $dest === true ) {
-               $dest = 'all';
-       } elseif ( $dest === false ) {
-               $dest = 'log';
-       }
-
        $text = trim( $text );
 
        // Inline logic from deprecated wfDebugTimer()
@@ -1067,16 +1055,6 @@ function wfDebug( $text, $dest = 'all', array $context = array() ) {
                );
        }
 
-       if ( $dest === 'all' ) {
-               $prefix = '';
-               if ( $wgDebugTimestamps ) {
-                       // Prepend elapsed request time and real memory usage with two
-                       // trailing spaces.
-                       $prefix = "{$context['seconds_elapsed']} {$context['memory_used']}  ";
-               }
-               MWDebug::debugMsg( "{$prefix}{$text}" );
-       }
-
        if ( $wgDebugLogPrefix !== '' ) {
                $context['prefix'] = $wgDebugLogPrefix;
        }
@@ -1181,10 +1159,6 @@ function wfDebugLog(
 
        $text = trim( $text );
 
-       if ( $dest === 'all' ) {
-               MWDebug::debugMsg( "[{$logGroup}] {$text}\n" );
-       }
-
        $logger = MWLoggerFactory::getInstance( $logGroup );
        $context['private'] = ( $dest === 'private' );
        $logger->info( $text, $context );
index 8799225..ed77729 100644 (file)
@@ -151,10 +151,6 @@ class Html {
                        } else {
                                $attrs['class'] = 'mw-ui-input';
                        }
-
-                       // Note that size can effect the desired width rendering of mw-ui-input elements
-                       // so it is removed. Left intact when mediawiki ui not enabled.
-                       unset( $attrs['size'] );
                }
                return $attrs;
        }
index a22f280..65904a0 100644 (file)
@@ -79,7 +79,7 @@ class TemplateParser {
         */
        public function getTemplate( $templateName ) {
                // If a renderer has already been defined for this template, reuse it
-               if ( isset( $this->renderers[$templateName] ) ) {
+               if ( isset( $this->renderers[$templateName] ) && is_callable( $this->renderers[$templateName] ) ) {
                        return $this->renderers[$templateName];
                }
 
@@ -134,6 +134,9 @@ class TemplateParser {
                }
 
                $renderer = eval( $code );
+               if ( !is_callable( $renderer ) ) {
+                       throw new RuntimeException( "Requested template, {$templateName}, is not callable" );
+               }
                return $this->renderers[$templateName] = $renderer;
        }
 
index dff6a4f..c74715d 100644 (file)
        "apihelp-managetags-description": "Effectuer des tâches de gestion relatives à la modification des balises.",
        "apihelp-managetags-param-operation": "Quelle opération effectuer :\n;create:Créer une nouvelle balise de modification pour un usage manuel.\n;delete:Supprimer une balise de modification de la base de données, y compris la suppression de la marque de toutes les révisions, entrées de modification récente et entrées de journal dans lesquelles elle serait utilisée.\n;activate:Activer une balise de modification, permettant aux utilisateurs de l’appliquer manuellement.\n;deactivate:Désactiver une balise de modification, empêchant les utilisateurs de l’appliquer manuellement.",
        "apihelp-managetags-param-tag": "Balise à créer, supprimer, activer ou désactiver. Pour la création de balise, elle ne doit pas exister. Pour la suppression de balise, elle doit exister. Pour l’activation de balise, elle doit exister et ne pas être utilisée par une extension. Pour la désactivation de balise, elle doit être actuellement active et définie manuellement.",
+       "apihelp-managetags-param-reason": "Un motif facultatif pour créer, supprimer, activer ou désactiver la balise.",
+       "apihelp-managetags-param-ignorewarnings": "S’il faut ignorer tout avertissement qui se produirait au cours de l’opération.",
+       "apihelp-managetags-example-create": "Créer une balise nommée <kbd>pourriel</kbd> avec le motif <kbd>À utiliser lors de la revue des modifications</kbd>",
+       "apihelp-managetags-example-delete": "Supprimer la balise <kbd>vandlaisme</kbd> avec le motif <kbd>Mal épelé</kbd>",
+       "apihelp-managetags-example-activate": "Activer une balise nommée <kbd>pourriel</kbd> avec le motif <kbd>À utiliser dans la revue des modifications</kbd>",
+       "apihelp-managetags-example-deactivate": "Désactiver une balise nommée <kbd>pourriel</kbd> avec le motif <kbd>Plus nécessaire</kbd>",
        "apihelp-move-description": "Déplacer une page.",
-       "apihelp-move-param-from": "Titre de la page que vous voulez déplacer. Impossible de l’utiliser avec $1fromid.",
-       "apihelp-move-param-fromid": "ID de la page que vous voulez déplacer. Impossible à utiliser avec $1from.",
+       "apihelp-move-param-from": "Titre de la page à renommer. Impossible de l’utiliser avec <var>$1fromid</var>.",
+       "apihelp-move-param-fromid": "ID de la page à renommer. Impossible à utiliser avec <var>$1from</var>.",
        "apihelp-move-param-to": "Titre de la page renommée.",
        "apihelp-move-param-reason": "Motif du renommage.",
        "apihelp-move-param-movetalk": "Renommer la page de discussion, si elle existe.",
        "apihelp-parse-param-pst": "Faire une transformation avant enregistrement de l’entrée avant de l’analyser. Valide uniquement quand utilisé avec du texte.",
        "apihelp-parse-param-onlypst": "Faire une transformation avant enregistrement (PST) de l’entrée, mais ne pas l’analyser. Renvoie le même wikitexte, après que la PST a été appliquée. Valide uniquement quand utilisé avec <var>$1text</var>.",
        "apihelp-parse-param-effectivelanglinks": "Inclut les liens de langue fournis par les extensions (à utiliser avec <kbd>$1prop=langlinks</kbd>).",
-       "apihelp-parse-param-section": "Récupérer uniquement le contenu de ce numéro de section.",
+       "apihelp-parse-param-section": "Récupérer uniquement le contenu de ce numéro de section ou quand <kbd>nouveau</kbd> génère une nouvelle section.\n\nLa <kbd>nouvelle</kbd> section est mise à l’honneur uniquement quand <var>text</var> est spécifié.",
+       "apihelp-parse-param-sectiontitle": "Nouveau titre de section quand <var>section</var> vaut <kbd>nouveau</kbd>.\n\nÀ la différence de la modification de page, cela ne revient pas à <var>summary</var> quand il est omis ou vide.",
        "apihelp-parse-param-disablepp": "Désactiver le rapport PP de la sortie de l’analyseur.",
        "apihelp-parse-param-disableeditsection": "Désactiver les liens de modification de section de la sortie de l’analyseur.",
        "apihelp-parse-param-generatexml": "Générer un arbre d’analyse XML (nécessite le modèle de contenu <code>$1</code>).",
        "apihelp-query+imageusage-param-namespace": "L’espace de noms à énumérer.",
        "apihelp-query+imageusage-param-dir": "La direction dans laquelle lister.",
        "apihelp-query+imageusage-param-filterredir": "Comment filtrer les redirections. Si mis à nonredirects quand $1redirect est activé, cela ne s’appliquera qu’au second niveau.",
-       "apihelp-query+imageusage-param-limit": "Combien de pages renvoyer au total. Si $1redirect est activé, la limite s’applique à chaque niveau séparément (ce qui veut dire que vous pouvez obtenir jusqu’à 2 * limite résultats).",
+       "apihelp-query+imageusage-param-limit": "Combien de pages renvoyer au total. Si <var>$1redirect</var> est activé, la limite s’applique à chaque niveau séparément (ce qui veut dire que jusqu’à 2 * <var>$1limit</var> résultats peuvent être renvoyés).",
        "apihelp-query+imageusage-param-redirect": "Si le lien vers une page est une redirection, trouver toutes les pages qui ont aussi un lien vers cette redirection. La limite maximale est divisée par deux.",
        "apihelp-query+imageusage-example-simple": "Afficher les pages utilisant [[:File:Albert Einstein Head.jpg]]",
        "apihelp-query+imageusage-example-generator": "Obtenir des informations sur les pages utilisant [[:File:Albert Einstein Head.jpg]]",
        "apihelp-query+info-paramvalue-prop-readable": "Si l’utilisateur peut lire cette page.",
        "apihelp-query+info-paramvalue-prop-preload": "Fournit le texte renvoyé par EditFormPreloadText.",
        "apihelp-query+info-paramvalue-prop-displaytitle": "Fournit la manière dont le titre de la page est réellement affiché.",
+       "apihelp-query+info-param-testactions": "Tester si l’utilisateur actuel peut effectuer certaines actions sur la page.",
        "apihelp-query+info-param-token": "Utiliser plutôt [[Special:ApiHelp/query+tokens|action=query&meta=tokens]].",
        "apihelp-query+info-example-simple": "Obtenir des informations sur la page <kbd>Main Page</kbd>.",
        "apihelp-query+info-example-protection": "Obtenir des informations générale et de protection sur la page <kbd>Main Page</kbd>.",
        "apihelp-query+links-param-dir": "La direction dans laquelle lister.",
        "apihelp-query+links-example-simple": "Obtenir les liens de la page <kbd>Main Page</kbd>",
        "apihelp-query+links-example-generator": "Obtenir des informations sur tous les liens de page dans <kbd>Main Page</kbd>.",
-       "apihelp-query+links-example-namespaces": "Obtenir les liens de [[Main Page]] dans les espaces de nom Utilisateur et Modèle",
+       "apihelp-query+links-example-namespaces": "Obtenir les liens de la page <kbd>Accueil</kbd> dans les espaces de nom {{ns:user}} et {{ns:template}}.",
        "apihelp-query+linkshere-description": "Trouver toutes les pages ayant un lien vers les pages données.",
        "apihelp-query+linkshere-param-prop": "Quelles propriétés obtenir :\n;pageid:ID de chaque page.\n;title:Titre de chaque page.\n;redirect:Indique si la page est une redirection.",
        "apihelp-query+linkshere-param-namespace": "Inclure uniquement les pages dans ces espaces de nom.",
        "apihelp-query+querypage-param-page": "Le nom de la page spéciale. Remarque, ce nom est sensible à la casse.",
        "apihelp-query+querypage-param-limit": "Nombre de résultats à renvoyer.",
        "apihelp-query+querypage-example-ancientpages": "Renvoyer les résultats de [[Special:Ancientpages]].",
-       "apihelp-query+random-description": "Obtenir un ensemble de pages au hasard.\n\nLes pages sont listées dans un ordre prédéterminé, seul le point de départ est aléatoire. Par exemple, cela signifie que si la première page sur votre liste est « Accueil », la seconde sera *toujours* « Liste des singes de fiction », la troisième « Liste de personnes figurant sur les timbres de Vanuatu », etc.\n\nSi le nombre de page dans l’espace de nom est inférieur à $1limit, vous obtiendrez moins de page. Vous n'obtiendrez jamais deux fois la même page.",
+       "apihelp-query+random-description": "Obtenir un ensemble de pages au hasard.\n\nLes pages sont listées dans un ordre prédéterminé, seul le point de départ est aléatoire. Par exemple, cela signifie que si la première page dans la liste est <samp>Accueil</samp>, la seconde sera <em>toujours</em> <samp>Liste des singes de fiction</samp>, la troisième <samp>Liste de personnes figurant sur les timbres de Vanuatu</samp>, etc.\n\nSi le nombre de page dans l’espace de nom est inférieur à <var>$1limit</var>, moins de pages seront renvoyées. La même page ne sera jamais renvoyée deux fois.",
        "apihelp-query+random-param-namespace": "Renvoyer seulement des pages de ces espaces de noms.",
        "apihelp-query+random-param-limit": "Limite sur le nombre de pages aléatoires renvoyées.",
        "apihelp-query+random-param-redirect": "Charger une redirection aléatoire plutôt qu’une page aléatoire.",
        "apihelp-query+search-example-text": "Rechercher des textes pour <kbd>signification</kbd>.",
        "apihelp-query+search-example-generator": "Obtenir les informations sur les pages renvoyées par une recherche de <kbd>signification</kbd>.",
        "apihelp-query+siteinfo-description": "Renvoyer les informations générales sur le site.",
-       "apihelp-query+siteinfo-param-prop": "Quelles informations obtenir :\n;general:Information globale du système.\n;namespaces:Liste des espaces de nom déclarés et leur nom canonique.\n;namespacealiases:Liste des alias des espaces de nom déclarés.\n;specialpagealiases:Liste des alias des pages spéciales.\n;magicwords:Liste des mots magiques et leurs alias.\n;statistics:Renvoie les statistiques du site.\n;interwikimap:Renvoie la correspondance interwiki (éventuellement filtrée, éventuellement localisée en utilisant <var>$1inlanguagecode</var>).\n;dbrepllag:Renvoie le serveur de base de donnée avec la plus grande latence de réplication.\n;usergroups:Renvoie les groupes utilisateur et les droits associés.\n;extensions:Renvoie les extensions installées sur le wiki.\n;fileextensions:Renvoie la liste des extensions de fichier autorisées au téléchargement.\n;rightsinfo:Renvoie l’information sur les droits du wiki (sa licence), si elle est disponible.\n;restrictions:Renvoie l’information sur les types de restriction disponibles (protection).\n;languages:Renvoie une liste des langues que supporte MédiaWiki (éventuellement localisé en utilisant <var>$1inlanguagecode</var>).\n;skins:Renvoie une liste de tous les habillages activés (éventuellement localisé en utilisant <var>$1inlanguagecode</var>, sinon dans la langue du contenu).\n;extensiontags:Renvoie une liste des balises d’extension de l’analyseur.\n;functionhooks:Renvoie une liste des accroches de fonction de l’analyseur.\n;showhooks:Renvoie une liste de toutes les accroches souscrites (contenu de <var>[[mw:Manual:$wgHooks|$wgHooks]]</var>).\n;variables:Renvoie une liste des IDs de variable.\n;protocols:Renvoie une liste des protocoles qui sont autorisés dans les liens externes.\n;defaultoptions:Renvoie les valeurs par défaut pour les préférences utilisateur.",
+       "apihelp-query+siteinfo-param-prop": "Quelles informations obtenir :\n;general:Information globale du système.\n;namespaces:Liste des espaces de nom déclarés et leur nom canonique.\n;namespacealiases:Liste des alias des espaces de nom déclarés.\n;specialpagealiases:Liste des alias des pages spéciales.\n;magicwords:Liste des mots magiques et leurs alias.\n;statistics:Renvoie les statistiques du site.\n;interwikimap:Renvoie la correspondance interwiki (éventuellement filtrée, éventuellement localisée en utilisant <var>$1inlanguagecode</var>).\n;dbrepllag:Renvoie le serveur de base de donnée avec la plus grande latence de réplication.\n;usergroups:Renvoie les groupes utilisateur et les droits associés.\n;libraries:Renvoie les bibliothèques installées sur le wiki.\n;extensions:Renvoie les extensions installées sur le wiki.\n;fileextensions:Renvoie la liste des extensions de fichier autorisées au téléchargement.\n;rightsinfo:Renvoie l’information sur les droits du wiki (sa licence), si elle est disponible.\n;restrictions:Renvoie l’information sur les types de restriction disponibles (protection).\n;languages:Renvoie une liste des langues que supporte MédiaWiki (éventuellement localisé en utilisant <var>$1inlanguagecode</var>).\n;skins:Renvoie une liste de tous les habillages activés (éventuellement localisé en utilisant <var>$1inlanguagecode</var>, sinon dans la langue du contenu).\n;extensiontags:Renvoie une liste des balises d’extension de l’analyseur.\n;functionhooks:Renvoie une liste des accroches de fonction de l’analyseur.\n;showhooks:Renvoie une liste de toutes les accroches souscrites (contenu de <var>[[mw:Manual:$wgHooks|$wgHooks]]</var>).\n;variables:Renvoie une liste des IDs de variable.\n;protocols:Renvoie une liste des protocoles qui sont autorisés dans les liens externes.\n;defaultoptions:Renvoie les valeurs par défaut pour les préférences utilisateur.",
        "apihelp-query+siteinfo-param-filteriw": "Renvoyer uniquement les entrées locales ou uniquement les non locales de la correspondance interwiki.",
        "apihelp-query+siteinfo-param-showalldb": "Lister tous les serveurs de base de données, pas seulement celui avec la plus grande latence.",
        "apihelp-query+siteinfo-param-numberingroup": "Liste le nombre d’utilisateurs dans les groupes.",
        "apihelp-query+stashimageinfo-example-params": "Renvoie les vignettes pour deux fichiers mis en réserve",
        "apihelp-query+tags-description": "Lister les balises de modification.",
        "apihelp-query+tags-param-limit": "Le nombre maximal de balises à lister.",
-       "apihelp-query+tags-param-prop": "Quelles propriétés récupérer :\n;name:Ajoute le nom de la balise.\n;displayname:Ajoute le message système pour la balise.\n;description:Ajoute la description de la balise.\n;hitcount:Ajoute le nombre de révisions qui ont cette balise.\n;defined:Indique si la balise est définie.",
+       "apihelp-query+tags-param-prop": "Quelles propriétés récupérer :\n;name:Ajoute le nom de la balise.\n;displayname:Ajoute le message système pour la balise.\n;description:Ajoute la description de la balise.\n;hitcount:Ajoute le nombre de révisions et d’entrées du journal qui ont cette balise.\n;defined:Indique si la balise est définie.\n;source:Obtient les sources de la balise, ce qui comprend <samp>extension</samp> pour les balises définies par une extension et <samp>manual</samp> pour les balises pouvant être appliquées manuellement par les utilisateurs.\n;active:Si la balise est encore appliquée.",
        "apihelp-query+tags-example-simple": "Lister les balises disponibles",
        "apihelp-query+templates-description": "Renvoie toutes les pages incluses dans les pages fournies.",
        "apihelp-query+templates-param-namespace": "Afficher les modèles uniquement dans ces espaces de nom.",
        "apihelp-query+templates-param-limit": "Combien de modèles renvoyer.",
        "apihelp-query+templates-param-templates": "Lister uniquement ces modèles. Utile pour vérifier si une certaine page utilise un modèle donné.",
        "apihelp-query+templates-param-dir": "La direction dans laquelle lister.",
-       "apihelp-query+templates-example-simple": "Obtenir les modèles de [[Main Page]]",
+       "apihelp-query+templates-example-simple": "Obtenir les modèles utilisés sur la page <kbd>Accueil</kbd>.",
        "apihelp-query+templates-example-generator": "Obtenir des informations sur les pages modèle utilisé sur <kbd>Main Page</kbd>.",
        "apihelp-query+templates-example-namespaces": "Obtenir les pages des espaces de nom {{ns:user}} et {{ns:template}} qui sont inclues dans la page <kdb>Main Page<kdb>.",
        "apihelp-query+tokens-description": "Récupère les jetons pour les actions de modification de données.",
        "apihelp-query+users-param-users": "Une liste des utilisateurs sur lesquels obtenir de l’information.",
        "apihelp-query+users-param-token": "Utiliser plutôt <kbd>[[Special:ApiHelp/query+tokens|action=query&meta=tokens]]</kbd>.",
        "apihelp-query+users-example-simple": "Renvoyer des informations pour l'utilisateur <kbd>Exemple</kbd>.",
-       "apihelp-query+watchlist-description": "Obtenir les modifications récentes des pages dans la liste de suivi de l’utilisateur connecté.",
+       "apihelp-query+watchlist-description": "Obtenir les modifications récentes des pages dans la liste de suivi de l’utilisateur actuel.",
        "apihelp-query+watchlist-param-allrev": "Inclure les multiples révisions de la même page dans l’intervalle de temps fourni.",
        "apihelp-query+watchlist-param-start": "L’horodatage auquel démarrer l’énumération.",
        "apihelp-query+watchlist-param-end": "L’horodatage auquel arrêter l’énumération.",
        "apihelp-query+watchlist-example-generator": "Chercher l’information de la page sur les pages récemment modifiées de la liste de suivi de l’utilisateur actuel",
        "apihelp-query+watchlist-example-generator-rev": "Chercher l’information de la révision pour les modifications récentes des pages de la liste de suivi de l’utilisateur actuel",
        "apihelp-query+watchlist-example-wlowner": "Lister la révision de tête des pages récemment modifiées de la liste de suivi de l'utilisateur <kbd>Exemple</kbd>.",
-       "apihelp-query+watchlistraw-description": "Obtenir toutes les pages de la liste de suivi de l’utilisateur connecté.",
+       "apihelp-query+watchlistraw-description": "Obtenir toutes les pages de la liste de suivi de l’utilisateur actuel.",
        "apihelp-query+watchlistraw-param-namespace": "Lister uniquement les pages dans les espaces de nom fournis.",
        "apihelp-query+watchlistraw-param-limit": "Combien de résultats renvoyer au total par requête.",
        "apihelp-query+watchlistraw-param-prop": "Quelles propriétés supplémentaires obtenir :\n;changed:Ajoute l’horodatage de la dernière notification de l’utilisateur à propos de la modification.",
        "apihelp-revisiondelete-example-revision": "Masquer le contenu de la révision <kbd>12345</kbd> de la page <kbd>Main Page</kbd>",
        "apihelp-revisiondelete-example-log": "Masquer toutes les données de l’entrée de journal <kbd>67890</kbd> avec le motif <kbd>Violation de Biographie de Personne Vivante</kbd>.",
        "apihelp-rollback-description": "Annuler la dernière modification de la page.\n\nSi le dernier utilisateur à avoir modifié la page a fait plusieurs modifications sur une ligne, elles seront toutes annulées.",
-       "apihelp-rollback-param-title": "Titre de la page que vous voulez restaurer. Impossible à utiliser avec $1pageid.",
-       "apihelp-rollback-param-pageid": "ID de la page que vous voulez restaurer. Impossible à utiliser avec $1title.",
+       "apihelp-rollback-param-title": "Titre de la page à restaurer. Impossible à utiliser avec <var>$1pageid</var>.",
+       "apihelp-rollback-param-pageid": "ID de la page à restaurer. Impossible à utiliser avec <var>$1title</var>.",
        "apihelp-rollback-param-user": "Nom de l’utilisateur dont les modifications doivent être annulées.",
        "apihelp-rollback-param-summary": "Personnaliser le résumé de la modification. S’il est vide, le résumé par défaut sera utilisé.",
        "apihelp-rollback-param-markbot": "Marquer les modifications annulées et les modifications annulées comme robot.",
-       "apihelp-rollback-param-watchlist": "Ajouter ou supprimer la page de votre liste de suivi sans condition, utiliser les préférences ou ne pas modifier le suivi.",
+       "apihelp-rollback-param-watchlist": "Ajouter ou supprimer la page de la liste de suivi de l’utilisateur actuel sans condition, utiliser les préférences ou ne pas modifier le suivi.",
        "apihelp-rollback-example-simple": "Annuler les dernières modifications à [<kbd>Main Page</kbd> par l’utilisateur <kbd>Exemple</kbd>.",
        "apihelp-rollback-example-summary": "Annuler les dernières modifications de la page <kbd>Main Page</kbd> par l’utilisateur à l’adresse IP <kbd>192.0.2.5</kbd> avec le résumé <kbd>Annulation de vandalisme<kbd>, et marquer ces modifications et l’annulation comme modifications de robots.",
        "apihelp-rsd-description": "Exporter un schéma RSD (Découverte Très Simple).",
        "apihelp-undelete-param-reason": "Motif de restauration.",
        "apihelp-undelete-param-timestamps": "Horodatages des révisions à restaurer. Si <var>$1timestamps</var> et <var>$1fileids</var> sont vides, toutes seront restaurées.",
        "apihelp-undelete-param-fileids": "IDs des révisions de fichier à restaurer. Si <var>$1timestamps</var> et <var>$1fileids</var> sont vides, toutes seront restaurées.",
-       "apihelp-undelete-param-watchlist": "Ajouter ou supprimer la page de votre liste de suivi sans condition, utiliser les préférences ou ne pas modifier le suivi.",
+       "apihelp-undelete-param-watchlist": "Ajouter ou supprimer la page de la liste de suivi de l’utilisateur actuel sans condition, utiliser les préférences ou ne pas modifier le suivi.",
        "apihelp-undelete-example-page": "Annuler la suppression de la page <kbd>Main Page</kbd>.",
        "apihelp-undelete-example-revisions": "Annuler la suppression de deux révisions de la page <kbd>Main Page</kbd>.",
        "apihelp-upload-description": "Télécharger un fichier, ou obtenir l’état des téléchargements en cours.\n\nPlusieurs méthodes sont disponibles :\n* Télécharger directement le contenu du fichier, en utilisant le paramètre <var>$1file</var>.\n* Télécharger le fichier par morceaux, en utilsiant les paramètres <var>$1filesize</var>, <var>$1chunk</var>, and <var>$1offset</var>.* Pour que le serveur MédiaWiki cherche un fichier depuis une URL, utiliser le paramètre <var>$1url</var>.\n* Terminer un téléchargement précédent qui a échoué à cause d’avertissements, en utilisant le paramètre <var>$1filekey</var>.\nNoter que le POST HTTP doit être fait comme un téléchargement de fichier (par ex. en utilisant <code>multipart/form-data</code>) en envoyant le <code>multipart/form-data</code>.",
        "apihelp-upload-param-comment": "Télécharger le commentaire. Utilisé aussi comme texte de la page initiale pour les nouveaux fichiers si <var>$1text</var> n’est pas spécifié.",
        "apihelp-upload-param-text": "Texte de page initiale pour les nouveaux fichiers.",
        "apihelp-upload-param-watch": "Suivre la page.",
-       "apihelp-upload-param-watchlist": "Ajouter ou supprimer sans condition la page de votre liste de suivi, utiliser les préférences ou ne pas changer le suivi.",
+       "apihelp-upload-param-watchlist": "Ajouter ou supprimer sans condition la page de la liste de suivi de l’utilisateur actuel, utiliser les préférences ou ne pas changer le suivi.",
        "apihelp-upload-param-ignorewarnings": "Ignorer tous les avertissements.",
        "apihelp-upload-param-file": "Contenu du fichier.",
        "apihelp-upload-param-url": "URL où chercher le fichier.",
        "apihelp-wddx-description": "Extraire les données au format WDDX.",
        "apihelp-wddxfm-description": "Extraire les données au format WDDX (affiché proprement en HTML).",
        "apihelp-xml-description": "Extraire les données au format XML.",
-       "apihelp-xml-param-xslt": "Si spécifié, ajoute &lt;xslt&gt; comme feuille de style. Ce devrait être une page du wiki dans l’espace de noms MediaWiki dont le nom de page se termine par « .xsl ».",
+       "apihelp-xml-param-xslt": "Si spécifié, ajoute la page nommée comme une feuille de style XSL. La valeur doit être un titre dans l’espace de noms {{ns:mediawiki}} se terminant par <code>.xsl</code>.",
        "apihelp-xml-param-includexmlnamespace": "Si spécifié, ajoute un espace de noms XML.",
        "apihelp-xmlfm-description": "Extraire les données au format XML (affiché proprement en HTML).",
        "apihelp-yaml-description": "Extraire les données au format YAML.",
        "apihelp-yamlfm-description": "Extraire les données YAML (affiché proprement en HTML).",
        "api-format-title": "Résultat de l’API de MédiaWiki",
-       "api-format-prettyprint-header": "Vous regardez la représentation HTML du format $1. HTML est utile pour le débogage, mais inapproprié pour être utilisé dans une application.$2\n\nSpécifiez le paramètre format pour modifier le format de sortie. Pour voir la représentation non HTML du format $1, mettez format=$2.\n\nVoyez la [https://www.mediawiki.org/wiki/API documentation complète], ou l’ [[Special:ApiHelp/main|aide de l’API]] pour plus d’information.",
+       "api-format-prettyprint-header": "Voici la représentation HTML du format $1. HTML est utile pour le débogage, mais inapproprié pour être utilisé dans une application.$2\n\nSpécifiez le paramètre <var>format</var> pour modifier le format de sortie. Pour voir la représentation non HTML du format $1, mettez <kbd>format=$2</kbd>.\n\nVoyez la [[mw:API|documentation complète]], ou l’ [[Special:ApiHelp/main|aide de l’API]] pour plus d’information.",
        "api-orm-param-props": "Champs à rechercher.",
        "api-orm-param-limit": "Nombre maximal de lignes à renvoyer.",
        "api-pageset-param-titles": "Une liste des titres sur lesquels travailler.",
index b6c0781..acb3b61 100644 (file)
        "apihelp-expandtemplates-param-title": "Título da páxina.",
        "apihelp-expandtemplates-param-text": "Sintaxis wiki a converter.",
        "apihelp-expandtemplates-param-revid": "ID de revisión, para <nowiki>{{REVISIONID}}</nowiki> e variables similares.",
+       "apihelp-expandtemplates-param-prop": "Pezas de información a retornar:\n;wikitext:O texto wiki expandido.\n;categories:Calquer categoría presente na entrada que non estea representada na saída do texto wiki\n;properties:Propiedades da páxina definidas por palabras máxicas expandidas no texto wiki\n;volatile:Definir se a saída é volátil e se non debe usarse noutra parte da páxina.\n;ttl:Tempo máximo a partir do cal os cachés do resultado deben invalidarse.\n;parsetree:O análise sintáctico en árbore do XML de entrada.\nTeña en conta que se non se selecciona ningún valor o resultado conterá o texto wiki, pero a saída estará nun formato desprezado.",
        "apihelp-expandtemplates-param-includecomments": "Cando queria incluír comentarios HTML na saída.",
        "apihelp-expandtemplates-param-generatexml": "Xenerar árbore de análise XML (reemprazado por $1prop=parsetree).",
        "apihelp-expandtemplates-example-simple": "Expandir o wikitexto <kbd><nowiki>{{Project:Sandbox}}</nowiki></kbd>.",
index 085113d..277f368 100644 (file)
@@ -7,6 +7,11 @@
                ]
        },
        "apihelp-block-description": "Blocca  un utente.",
+       "apihelp-emailuser-description": "Manda un'e-mail ad un utente.",
+       "apihelp-emailuser-param-ccme": "Mandami una copia di questa mail.",
+       "apihelp-expandtemplates-description": "Espandi tutti i template nel wikitesto.",
+       "apihelp-expandtemplates-param-title": "Titolo della pagina.",
+       "apihelp-expandtemplates-param-text": "Wikitesto da convertire.",
        "apihelp-query+recentchanges-example-simple": "Elenco modifiche recenti.",
        "apihelp-upload-example-url": "Carica da un URL.",
        "api-help-parameters": "{{PLURAL:$1|Parametro|Parametri}}:",
index d180d81..4dcc433 100644 (file)
        "apihelp-login-param-password": "Paßwoot.",
        "apihelp-login-param-domain": "De Domaijn (kann fott bliehve)",
        "apihelp-login-example-login": "Enlogge.",
+       "apihelp-logout-example-logout": "Donn dä aktoälle Metmaacher ußlogge.",
+       "apihelp-move-description": "Donn en Sigg ömbenänne",
+       "apihelp-move-param-to": "De neue Övverschreff för di Sigg drop ömzebenänne.",
+       "apihelp-move-param-reason": "Der jrond för di Sigg ömzebenänne.",
+       "apihelp-move-param-movetalk": "Donn de Klaafsigg ömbenänne, wann et se jitt.",
+       "apihelp-move-param-movesubpages": "Donn de Ongersigge ömbenänne, wann müjjelesch.",
+       "apihelp-move-param-noredirect": "Donn kein Ömleidong aanlähje.",
+       "apihelp-move-param-watch": "Donn de Sigg un de Ömleijdong op dem aktoälle Metmaacher sing Oppaßleß.",
+       "apihelp-move-param-unwatch": "Donn de Sigg un de Ömleijdong uß dem aktoälle Metmaacher sing Oppaßleß eruß nämme.",
+       "apihelp-move-param-watchlist": "Donn di Sigg en dem aktoälle Metmaacher sing Oppaßleß udder nemm se eruß, donn de Enschtällonge nämme udder donn de Oppaßleß nid ändere.",
+       "apihelp-move-param-ignorewarnings": "Donn alle Warnonge övverjonn",
+       "apihelp-move-example-move": "Donn <kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">Schlääschte Övverschreff</kbd> nach <kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">Johde Övverschreff</kbd> önmännde, der ohne en Ömleijdong aanzelähje.",
+       "apihelp-opensearch-param-search": "Noh wat söhke?",
+       "apihelp-opensearch-param-limit": "De hühßte Aanzahl vun Äjeebnesse för zeröck ze jävve",
+       "apihelp-opensearch-param-namespace": "En wällschem Appachtemang söhke.",
        "apihelp-protect-description": "Änder der Siggeschoz för en Sigg.",
        "apihelp-query+allmessages-param-prop": "Wat för en Eijeschaffte holle.",
        "apihelp-query+allmessages-param-filter": "Jiv blohß de Täxte un Nohreesche uß, woh heh dat Täxschtöck dren änthallde es.",
index 6f31d73..81e5941 100644 (file)
        "apihelp-parse-param-redirects": "如果<var>$1page</var>或<var>$1pageid</var>被设置为一个重定向,则解析它。",
        "apihelp-parse-param-oldid": "解析该修订版本的内容。覆盖<var>$1page</var>和<var>$1pageid</var>。",
        "apihelp-parse-param-effectivelanglinks": "包含由扩展提供的语言链接(用于与<kbd>$1prop=langlinks</kbd>一起使用)。",
+       "apihelp-parse-param-sectiontitle": "当<var>section</var>为<kbd>new</kbd>时新段落标题。\n\n不像页面编辑,当省略或为空时将不会备选为<var>summary</var>。",
        "apihelp-parse-param-disablepp": "从解析器输出中禁用PP报告。",
        "apihelp-parse-param-disableeditsection": "从解析器输出中禁用编辑段落链接。",
        "apihelp-parse-param-generatexml": "生成XML解析树(需要内容模型<code>$1</code>)。",
        "apihelp-query+allusers-param-dir": "排序方向。",
        "apihelp-query+allusers-param-group": "只包含指定组中的用户。",
        "apihelp-query+allusers-param-excludegroup": "排除指定组中的用户。",
+       "apihelp-query+allusers-param-limit": "返回的总计用户数。",
        "apihelp-query+allusers-param-witheditsonly": "只列出有编辑的用户。",
        "apihelp-query+allusers-param-activeusers": "只列出最近$1天内活跃的用户。",
        "apihelp-query+allusers-example-Y": "列出以<kbd>Y</kbd>开头的用户。",
+       "apihelp-query+backlinks-description": "查找所有链接至指定页面的页面。",
        "apihelp-query+backlinks-param-title": "要搜索的标题。不能与<var>$1pageid</var>一起使用。",
        "apihelp-query+backlinks-param-pageid": "要搜索的页面ID。不能与<var>$1title</var>一起使用。",
        "apihelp-query+backlinks-param-namespace": "要列举的名字空间。",
        "apihelp-query+imageinfo-paramvalue-prop-dimensions": "大小别名。",
        "apihelp-query+imageinfo-paramvalue-prop-sha1": "为文件加入SHA-1哈希值。",
        "apihelp-query+imageinfo-paramvalue-prop-mime": "添加文件的MIME类型。",
+       "apihelp-query+imageinfo-paramvalue-prop-mediatype": "添加文件媒体类型。",
+       "apihelp-query+imageinfo-param-start": "开始列举的时间戳。",
+       "apihelp-query+imageinfo-param-end": "列举的结束时间戳。",
        "apihelp-query+imageinfo-param-urlheight": "与$1urlwidth类似。",
+       "apihelp-query+imageinfo-param-metadataversion": "要使用的元数据版本。如果<kbd>latest</kbd>被指定,则使用最新版本。默认为<kbd>1</kbd>以便向下兼容。",
        "apihelp-query+imageinfo-param-localonly": "只看本地存储库的文件。",
        "apihelp-query+imageinfo-example-simple": "获取有关[[:File:Albert Einstein Head.jpg]]的当前版本的信息",
        "apihelp-query+imageinfo-example-dated": "获取有关[[:File:Albert Einstein Head.jpg]]自2008年以来版本的信息",
index e9a7022..8eccd5d 100644 (file)
@@ -33,7 +33,7 @@
        "apihelp-createaccount-param-password": "密碼 (若有設定 <var>$1mailpassword</var> 則可略過)。",
        "apihelp-createaccount-param-domain": "外部認証使用的網域 (選填)。",
        "apihelp-createaccount-param-token": "已取得帳號建立密鑰於第一次請求。",
-       "apihelp-createaccount-param-email": "使用者的電子郵件位址 (選填)。",
+       "apihelp-createaccount-param-email": "使用者的電子郵件地址 (選填) 。",
        "apihelp-createaccount-param-realname": "使用者的真實姓名 (選填)。",
        "apihelp-createaccount-param-mailpassword": "若設為其他值,將會以電子郵件寄送隨機密碼給使用者。",
        "apihelp-createaccount-param-reason": "建立帳號時選填的原因,會被記錄到日誌當中。",
index 48d4e8d..dc80a46 100644 (file)
@@ -309,11 +309,24 @@ class MWDebug {
         *
         * @since 1.19
         * @param string $str
+        * @param array $context
         */
-       public static function debugMsg( $str ) {
+       public static function debugMsg( $str, $context = array() ) {
                global $wgDebugComments, $wgShowDebug;
 
                if ( self::$enabled || $wgDebugComments || $wgShowDebug ) {
+                       if ( $context ) {
+                               $prefix = '';
+                               if ( isset( $context['prefix'] ) ) {
+                                       $prefix = $context['prefix'];
+                               } elseif ( isset( $context['channel'] ) && $context['channel'] !== 'wfDebug' ) {
+                                       $prefix = "[{$context['channel']}] ";
+                               }
+                               if ( isset( $context['seconds_elapsed'] ) && isset( $context['memory_used'] ) ) {
+                                       $prefix .= "{$context['seconds_elapsed']} {$context['memory_used']}  ";
+                               }
+                               $str = $prefix . $str;
+                       }
                        self::$debug[] = rtrim( UtfNormal::cleanUp( $str ) );
                }
        }
index be46c27..c53aeaa 100644 (file)
@@ -84,6 +84,8 @@ class MWLoggerLegacyLogger extends AbstractLogger {
                        $destination = self::destination( $this->channel, $message, $context );
                        self::emit( $text, $destination );
                }
+               // Add to debug toolbar
+               MWDebug::debugMsg( $message, array( 'channel' => $this->channel ) + $context );
        }
 
 
index be5ca7f..b4cced3 100644 (file)
@@ -842,7 +842,7 @@ class LocalFile extends File {
         * Refresh metadata in memcached, but don't touch thumbnails or squid
         */
        function purgeMetadataCache() {
-               $this->loadFromDB();
+               $this->loadFromDB( File::READ_LATEST );
                $this->saveToCache();
                $this->purgeHistory();
        }
index 128bfcc..c27c01e 100644 (file)
@@ -7,7 +7,7 @@
                ]
        },
        "config-desc": "साँचा लिए इंस्टॉलर",
-       "config-title": "मीडियाविकी इंस्टॉलर",
+       "config-title": "मीडियाविकी  $1 इंस्टॉलेशन",
        "config-information": "जानकारी",
        "config-localsettings-upgrade": "<code>LocalSettings.php</code> फ़ाइल पाया गया है ।\nइस स्थापना को अपग्रेड करने के लिए, नीचे दिए गए बॉक्स में <code>$wgUpgradeKey</code>  का मान दर्ज करें।\nआपको <code>LocalSettings.php</code> में मिल जाएगा।",
        "config-localsettings-cli-upgrade": "<code>LocalSettings.php</code> फ़ाइल पाया गया है ।\nइस स्थापना को अपग्रेड करने के लिए, बदले में कृपया करके ये चलाए <code>update.php</code>",
@@ -21,6 +21,7 @@
        "config-page-welcome": "मीडियाविकी पर आपका स्वागत है!",
        "config-page-dbconnect": "डेटाबेस से जुड़ें",
        "config-page-upgrade": "मौजूदा स्थापना का नवीनीकरण",
+       "config-page-dbsettings": "डेटाबेस सेटिंग्स",
        "config-page-name": "नाम",
        "config-page-options": "विकल्प",
        "config-page-install": "स्थापित करें",
index 07a6f35..0f2d7be 100644 (file)
        "config-admin-email-help": "在此輸入的電子郵件信箱可用來接收 Wiki 上其他使用者所傳送的訊息、重設您的密碼與通知監視清單中頁面更動。您可將此欄位留空。",
        "config-admin-error-user": "建立管理員帳號 \"<nowiki>$1</nowiki>\" 時發送內部錯誤。",
        "config-admin-error-password": "設定管理員 \"<nowiki>$1</nowiki>\" 的密碼時發送內部錯誤:<pre>$2</pre>",
-       "config-admin-error-bademail": "您輸入了不正確的電地址。",
+       "config-admin-error-bademail": "您輸入了不正確的電子郵件地址。",
        "config-subscribe": "訂閱 [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce 發佈公告郵寄清單]。",
        "config-subscribe-help": "這是一個用於發佈公告的低郵件量郵寄清單,內容包括重要的安全公告。\n您應該訂閱它並在 MediaWiki 發佈新版的時候更新系統。",
-       "config-subscribe-noemail": "您正嘗試不填寫電郵地址訂閱發佈公告郵寄清單。 \n請如果您希望訂閱郵寄清單,請提供一個有效的電郵地址。",
+       "config-subscribe-noemail": "您正嘗試不填寫電子郵件地址訂閱發佈公告郵寄清單。 \n請如果您希望訂閱郵寄清單,請提供一個有效的電子郵件地址。",
        "config-almost-done": "您快要完成了!\n您現在可以跳過其餘的設定項目並且立即安裝 Wiki。",
        "config-optional-continue": "多問我一些問題吧。",
        "config-optional-skip": "我已經不耐煩了,請趕緊安裝 Wiki。",
        "config-email-watchlist-help": "若使用者在個人偏好開啟了此功能,允許使用者收到與其監視清單有關的通知。",
        "config-email-auth": "開啟電子郵件身份認證",
        "config-email-auth-help": "若開啟此選項,使用者不論設定或者更改電子郵件地址,都必須透過收信的方式確認沒有問題。\n只有驗證過的電子郵件地址可以收到來自其他使用者或修改通知的信件。\n公開的 Wiki 會 <strong>建議</strong> 設定此選項,以防使用者濫用電子郵件功能。",
-       "config-email-sender": "電子郵件回覆址:",
+       "config-email-sender": "電子郵件回覆址:",
        "config-email-sender-help": "請輸入要用來做為外寄郵件的電子郵件回覆地址。\n該郵件地址會收到被拒收的信件。\n許多郵件伺服器會要求使用有效的網域名稱。",
        "config-upload-settings": "圖片和檔案上傳",
        "config-upload-enable": "開啟檔案上傳",
index 66bbb9c..1d84dc4 100644 (file)
@@ -49,8 +49,6 @@
 class JobQueueFederated extends JobQueue {
        /** @var HashRing */
        protected $partitionRing;
-       /** @var HashRing */
-       protected $partitionPushRing;
        /** @var array (partition name => JobQueue) reverse sorted by weight */
        protected $partitionQueues = array();
 
@@ -68,8 +66,6 @@ class JobQueueFederated extends JobQueue {
         *                          These configuration arrays are passed to JobQueue::factory().
         *                          The options set here are overridden by those passed to this
         *                          the federated queue itself (e.g. 'order' and 'claimTTL').
-        *  - partitionsNoPush    : List of partition names that can handle pop() but not push().
-        *                          This can be used to migrate away from a certain partition.
         *  - maxPartitionsTry    : Maximum number of times to attempt job insertion using
         *                          different partition queues. This improves availability
         *                          during failure, at the cost of added latency and somewhat
@@ -90,17 +86,10 @@ class JobQueueFederated extends JobQueue {
                // Get the full partition map
                $partitionMap = $params['partitionsBySection'][$section];
                arsort( $partitionMap, SORT_NUMERIC );
-               // Get the partitions jobs can actually be pushed to
-               $partitionPushMap = $partitionMap;
-               if ( isset( $params['partitionsNoPush'] ) ) {
-                       foreach ( $params['partitionsNoPush'] as $partition ) {
-                               unset( $partitionPushMap[$partition] );
-                       }
-               }
                // Get the config to pass to merge into each partition queue config
                $baseConfig = $params;
                foreach ( array( 'class', 'sectionsByWiki', 'maxPartitionsTry',
-                       'partitionsBySection', 'configByPartition', 'partitionsNoPush' ) as $o
+                       'partitionsBySection', 'configByPartition', ) as $o
                ) {
                        unset( $baseConfig[$o] ); // partition queue doesn't care about this
                }
@@ -114,12 +103,6 @@ class JobQueueFederated extends JobQueue {
                }
                // Ring of all partitions
                $this->partitionRing = new HashRing( $partitionMap );
-               // Get the ring of partitions to push jobs into
-               if ( count( $partitionPushMap ) === count( $partitionMap ) ) {
-                       $this->partitionPushRing = clone $this->partitionRing; // faster
-               } else {
-                       $this->partitionPushRing = new HashRing( $partitionPushMap );
-               }
        }
 
        protected function supportedOrders() {
@@ -196,7 +179,7 @@ class JobQueueFederated extends JobQueue {
 
        protected function doBatchPush( array $jobs, $flags ) {
                // Local ring variable that may be changed to point to a new ring on failure
-               $partitionRing = $this->partitionPushRing;
+               $partitionRing = $this->partitionRing;
                // Try to insert the jobs and update $partitionsTry on any failures.
                // Retry to insert any remaning jobs again, ignoring the bad partitions.
                $jobsLeft = $jobs;
@@ -332,12 +315,12 @@ class JobQueueFederated extends JobQueue {
        protected function doIsRootJobOldDuplicate( Job $job ) {
                $params = $job->getRootJobParams();
                $sigature = $params['rootJobSignature'];
-               $partition = $this->partitionPushRing->getLiveLocation( $sigature );
+               $partition = $this->partitionRing->getLiveLocation( $sigature );
                try {
                        return $this->partitionQueues[$partition]->doIsRootJobOldDuplicate( $job );
                } catch ( JobQueueError $e ) {
-                       if ( $this->partitionPushRing->ejectFromLiveRing( $partition, 5 ) ) {
-                               $partition = $this->partitionPushRing->getLiveLocation( $sigature );
+                       if ( $this->partitionRing->ejectFromLiveRing( $partition, 5 ) ) {
+                               $partition = $this->partitionRing->getLiveLocation( $sigature );
                                return $this->partitionQueues[$partition]->doIsRootJobOldDuplicate( $job );
                        }
                }
@@ -348,12 +331,12 @@ class JobQueueFederated extends JobQueue {
        protected function doDeduplicateRootJob( Job $job ) {
                $params = $job->getRootJobParams();
                $sigature = $params['rootJobSignature'];
-               $partition = $this->partitionPushRing->getLiveLocation( $sigature );
+               $partition = $this->partitionRing->getLiveLocation( $sigature );
                try {
                        return $this->partitionQueues[$partition]->doDeduplicateRootJob( $job );
                } catch ( JobQueueError $e ) {
-                       if ( $this->partitionPushRing->ejectFromLiveRing( $partition, 5 ) ) {
-                               $partition = $this->partitionPushRing->getLiveLocation( $sigature );
+                       if ( $this->partitionRing->ejectFromLiveRing( $partition, 5 ) ) {
+                               $partition = $this->partitionRing->getLiveLocation( $sigature );
                                return $this->partitionQueues[$partition]->doDeduplicateRootJob( $job );
                        }
                }
index 787f21e..968e424 100644 (file)
@@ -245,11 +245,11 @@ abstract class ImageHandler extends MediaHandler {
                if ( $pages === false || $pages <= 1 ) {
                        $msg = wfMessage( 'file-info-size' )->numParams( $file->getWidth(),
                                $file->getHeight() )->params( $size,
-                                       $file->getMimeType() )->parse();
+                                       '<span class="mime-type">' . $file->getMimeType() . '</span>' )->parse();
                } else {
                        $msg = wfMessage( 'file-info-size-pages' )->numParams( $file->getWidth(),
                                $file->getHeight() )->params( $size,
-                                       $file->getMimeType() )->numParams( $pages )->parse();
+                                       '<span class="mime-type">' . $file->getMimeType() . '</span>' )->numParams( $pages )->parse();
                }
 
                return $msg;
index 0103946..33aed34 100644 (file)
@@ -639,7 +639,7 @@ abstract class MediaHandler {
         */
        static function getGeneralLongDesc( $file ) {
                return wfMessage( 'file-info' )->sizeParams( $file->getSize() )
-                       ->params( $file->getMimeType() )->parse();
+                       ->params( '<span class="mime-type">' . $file->getMimeType() . '</span>' )->parse();
        }
 
        /**
@@ -861,4 +861,26 @@ abstract class MediaHandler {
        public function sanitizeParamsForBucketing( $params ) {
                return $params;
        }
+
+       /**
+        * Gets configuration for the file warning message. Return value of
+        * the following structure:
+        *   array(
+        *     'module' => 'example.filewarning.messages', // Required, module with messages loaded for the client
+        *     'messages' => array( // Required, array of names of messages
+        *       'main' => 'example-filewarning-main', // Required, main warning message
+        *       'header' => 'example-filewarning-header', // Optional, header for warning dialog
+        *       'footer' => 'example-filewarning-footer', // Optional, footer for warning dialog
+        *       'info' => 'example-filewarning-info', // Optional, text for more-information link (see below)
+        *     ),
+        *     'link' => 'http://example.com', // Optional, link for more information
+        *   )
+        *
+        * Returns null if no warning is necessary.
+        * @param File $file
+        * @return array|null
+        */
+       public function getWarningConfig( $file ) {
+               return null;
+       }
 }
index 504fa74..8f635cf 100644 (file)
@@ -505,6 +505,22 @@ class ImagePage extends Article {
 
                        $longDesc = $this->getContext()->msg( 'parentheses', $this->displayImg->getLongDesc() )->text();
 
+                       $handler = $this->displayImg->getHandler();
+
+                       // If this is a filetype with potential issues, warn the user.
+                       if ( $handler ) {
+                               $warningConfig = $handler->getWarningConfig( $this->displayImg );
+
+                               if ( $warningConfig !== null ) {
+                                       // The warning will be displayed via CSS and JavaScript.
+                                       // We just need to tell the client side what message to use.
+                                       $output = $this->getContext()->getOutput();
+                                       $output->addJsConfigVars( 'wgFileWarning', $warningConfig );
+                                       $output->addModules( $warningConfig['module'] );
+                                       $output->addModules( 'mediawiki.filewarning' );
+                               }
+                       }
+
                        $medialink = "[[Media:$filename|$linktext]]";
 
                        if ( !$this->displayImg->isSafeFile() ) {
diff --git a/includes/site/CachingSiteStore.php b/includes/site/CachingSiteStore.php
new file mode 100644 (file)
index 0000000..9243f12
--- /dev/null
@@ -0,0 +1,195 @@
+<?php
+
+/**
+ * Represents the site configuration of a wiki.
+ * Holds a list of sites (ie SiteList), with a caching layer.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @since 1.25
+ *
+ * @file
+ * @ingroup Site
+ *
+ * @license GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ * @author Katie Filbert < aude.wiki@gmail.com >
+ */
+class CachingSiteStore implements SiteStore {
+
+       /**
+        * @var SiteList|null
+        */
+       private $sites = null;
+
+       /**
+        * @var string|null
+        */
+       private $cacheKey;
+
+       /**
+        * @var int
+        */
+       private $cacheTimeout;
+
+       /**
+        * @var BagOStuff
+        */
+       private $cache;
+
+       /**
+        * @var SiteStore
+        */
+       private $siteStore;
+
+       /**
+        * @param SiteStore $siteStore
+        * @param BagOStuff $cache
+        * @param string|null $cacheKey
+        * @param int $cacheTimeout
+        */
+       public function __construct(
+               SiteStore $siteStore,
+               BagOStuff $cache,
+               $cacheKey = null,
+               $cacheTimeout = 3600
+       ) {
+               $this->siteStore = $siteStore;
+               $this->cache = $cache;
+               $this->cacheKey = $cacheKey;
+               $this->cacheTimeout = $cacheTimeout;
+       }
+
+       /**
+        * Constructs a cache key to use for caching the list of sites.
+        *
+        * This includes the concrete class name of the site list as well as a version identifier
+        * for the list's serialization, to avoid problems when unserializing site lists serialized
+        * by an older version, e.g. when reading from a cache.
+        *
+        * The cache key also includes information about where the sites were loaded from, e.g.
+        * the name of a database table.
+        *
+        * @see SiteList::getSerialVersionId
+        *
+        * @return string The cache key.
+        */
+       private function getCacheKey() {
+               if ( $this->cacheKey === null ) {
+                       $type = 'SiteList#' . SiteList::getSerialVersionId();
+                       $this->cacheKey = wfMemcKey( "sites/$type" );
+               }
+
+               return $this->cacheKey;
+       }
+
+       /**
+        * @see SiteStore::getSites
+        *
+        * @since 1.25
+        *
+        * @return SiteList
+        */
+       public function getSites() {
+               if ( $this->sites === null ) {
+                       $this->sites = $this->cache->get( $this->getCacheKey() );
+
+                       if ( !is_object( $this->sites ) ) {
+                               $this->sites = $this->siteStore->getSites();
+
+                               $this->cache->set( $this->getCacheKey(), $this->sites, $this->cacheTimeout );
+                       }
+               }
+
+               return $this->sites;
+       }
+
+       /**
+        * @see SiteStore::getSite
+        *
+        * @since 1.25
+        *
+        * @param string $globalId
+        *
+        * @return Site|null
+        */
+       public function getSite( $globalId ) {
+               $sites = $this->getSites();
+
+               return $sites->hasSite( $globalId ) ? $sites->getSite( $globalId ) : null;
+       }
+
+       /**
+        * @see SiteStore::saveSite
+        *
+        * @since 1.25
+        *
+        * @param Site $site
+        *
+        * @return bool Success indicator
+        */
+       public function saveSite( Site $site ) {
+               return $this->saveSites( array( $site ) );
+       }
+
+       /**
+        * @see SiteStore::saveSites
+        *
+        * @since 1.25
+        *
+        * @param Site[] $sites
+        *
+        * @return bool Success indicator
+        */
+       public function saveSites( array $sites ) {
+               if ( empty( $sites ) ) {
+                       return true;
+               }
+
+               $success = $this->siteStore->saveSites( $sites );
+
+               // purge cache
+               $this->reset();
+
+               return $success;
+       }
+
+       /**
+        * Purges the internal and external cache of the site list, forcing the list
+        * of sites to be reloaded.
+        *
+        * @since 1.25
+        */
+       public function reset() {
+               // purge cache
+               $this->cache->delete( $this->getCacheKey() );
+               $this->sites = null;
+       }
+
+       /**
+        * Clears the list of sites stored.
+        *
+        * @see SiteStore::clear()
+        *
+        * @return bool Success
+        */
+       public function clear() {
+               $this->reset();
+
+               return $this->siteStore->clear();
+       }
+
+}
diff --git a/includes/site/DBSiteStore.php b/includes/site/DBSiteStore.php
new file mode 100644 (file)
index 0000000..f167584
--- /dev/null
@@ -0,0 +1,345 @@
+<?php
+
+/**
+ * Represents the site configuration of a wiki.
+ * Holds a list of sites (ie SiteList), stored in the database.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @since 1.25
+ *
+ * @file
+ * @ingroup Site
+ *
+ * @license GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class DBSiteStore implements SiteStore {
+
+       /**
+        * @var SiteList|null
+        */
+       protected $sites = null;
+
+       /**
+        * @var ORMTable
+        */
+       protected $sitesTable;
+
+       /**
+        * @since 1.25
+        *
+        * @param ORMTable|null $sitesTable
+        */
+       public function __construct( ORMTable $sitesTable = null ) {
+               if ( $sitesTable === null ) {
+                       $sitesTable = $this->newSitesTable();
+               }
+
+               $this->sitesTable = $sitesTable;
+       }
+
+       /**
+        * @see SiteStore::getSites
+        *
+        * @since 1.25
+        *
+        * @return SiteList
+        */
+       public function getSites() {
+               $this->loadSites();
+
+               return $this->sites;
+       }
+
+       /**
+        * Returns a new Site object constructed from the provided ORMRow.
+        *
+        * @since 1.25
+        *
+        * @param ORMRow $siteRow
+        *
+        * @return Site
+        */
+       protected function siteFromRow( ORMRow $siteRow ) {
+
+               $site = Site::newForType( $siteRow->getField( 'type', Site::TYPE_UNKNOWN ) );
+
+               $site->setGlobalId( $siteRow->getField( 'global_key' ) );
+
+               $site->setInternalId( $siteRow->getField( 'id' ) );
+
+               if ( $siteRow->hasField( 'forward' ) ) {
+                       $site->setForward( $siteRow->getField( 'forward' ) );
+               }
+
+               if ( $siteRow->hasField( 'group' ) ) {
+                       $site->setGroup( $siteRow->getField( 'group' ) );
+               }
+
+               if ( $siteRow->hasField( 'language' ) ) {
+                       $site->setLanguageCode( $siteRow->getField( 'language' ) === ''
+                               ? null
+                               : $siteRow->getField( 'language' )
+                       );
+               }
+
+               if ( $siteRow->hasField( 'source' ) ) {
+                       $site->setSource( $siteRow->getField( 'source' ) );
+               }
+
+               if ( $siteRow->hasField( 'data' ) ) {
+                       $site->setExtraData( $siteRow->getField( 'data' ) );
+               }
+
+               if ( $siteRow->hasField( 'config' ) ) {
+                       $site->setExtraConfig( $siteRow->getField( 'config' ) );
+               }
+
+               return $site;
+       }
+
+       /**
+        * Get a new ORMRow from a Site object
+        *
+        * @since 1.25
+        *
+        * @param Site $site
+        *
+        * @return ORMRow
+        */
+       protected function getRowFromSite( Site $site ) {
+               $fields = array(
+                       // Site data
+                       'global_key' => $site->getGlobalId(), // TODO: check not null
+                       'type' => $site->getType(),
+                       'group' => $site->getGroup(),
+                       'source' => $site->getSource(),
+                       'language' => $site->getLanguageCode() === null ? '' : $site->getLanguageCode(),
+                       'protocol' => $site->getProtocol(),
+                       'domain' => strrev( $site->getDomain() ) . '.',
+                       'data' => $site->getExtraData(),
+
+                       // Site config
+                       'forward' => $site->shouldForward(),
+                       'config' => $site->getExtraConfig(),
+               );
+
+               if ( $site->getInternalId() !== null ) {
+                       $fields['id'] = $site->getInternalId();
+               }
+
+               return new ORMRow( $this->sitesTable, $fields );
+       }
+
+       /**
+        * Fetches the site from the database and loads them into the sites field.
+        *
+        * @since 1.25
+        */
+       protected function loadSites() {
+               $this->sites = new SiteList();
+
+               foreach ( $this->sitesTable->select() as $siteRow ) {
+                       $this->sites[] = $this->siteFromRow( $siteRow );
+               }
+
+               // Batch load the local site identifiers.
+               $ids = wfGetDB( $this->sitesTable->getReadDb() )->select(
+                       'site_identifiers',
+                       array(
+                               'si_site',
+                               'si_type',
+                               'si_key',
+                       ),
+                       array(),
+                       __METHOD__
+               );
+
+               foreach ( $ids as $id ) {
+                       if ( $this->sites->hasInternalId( $id->si_site ) ) {
+                               $site = $this->sites->getSiteByInternalId( $id->si_site );
+                               $site->addLocalId( $id->si_type, $id->si_key );
+                               $this->sites->setSite( $site );
+                       }
+               }
+       }
+
+       /**
+        * @see SiteStore::getSite
+        *
+        * @since 1.25
+        *
+        * @param string $globalId
+        *
+        * @return Site|null
+        */
+       public function getSite( $globalId ) {
+               if ( $this->sites === null ) {
+                       $this->sites = $this->getSites();
+               }
+
+               return $this->sites->hasSite( $globalId ) ? $this->sites->getSite( $globalId ) : null;
+       }
+
+       /**
+        * @see SiteStore::saveSite
+        *
+        * @since 1.25
+        *
+        * @param Site $site
+        *
+        * @return bool Success indicator
+        */
+       public function saveSite( Site $site ) {
+               return $this->saveSites( array( $site ) );
+       }
+
+       /**
+        * @see SiteStore::saveSites
+        *
+        * @since 1.25
+        *
+        * @param Site[] $sites
+        *
+        * @return bool Success indicator
+        */
+       public function saveSites( array $sites ) {
+               if ( empty( $sites ) ) {
+                       return true;
+               }
+
+               $dbw = $this->sitesTable->getWriteDbConnection();
+
+               $dbw->startAtomic( __METHOD__ );
+
+               $success = true;
+
+               $internalIds = array();
+               $localIds = array();
+
+               foreach ( $sites as $site ) {
+                       if ( $site->getInternalId() !== null ) {
+                               $internalIds[] = $site->getInternalId();
+                       }
+
+                       $siteRow = $this->getRowFromSite( $site );
+                       $success = $siteRow->save( __METHOD__ ) && $success;
+
+                       foreach ( $site->getLocalIds() as $idType => $ids ) {
+                               foreach ( $ids as $id ) {
+                                       $localIds[] = array( $siteRow->getId(), $idType, $id );
+                               }
+                       }
+               }
+
+               if ( $internalIds !== array() ) {
+                       $dbw->delete(
+                               'site_identifiers',
+                               array( 'si_site' => $internalIds ),
+                               __METHOD__
+                       );
+               }
+
+               foreach ( $localIds as $localId ) {
+                       $dbw->insert(
+                               'site_identifiers',
+                               array(
+                                       'si_site' => $localId[0],
+                                       'si_type' => $localId[1],
+                                       'si_key' => $localId[2],
+                               ),
+                               __METHOD__
+                       );
+               }
+
+               $dbw->endAtomic( __METHOD__ );
+
+               $this->reset();
+
+               return $success;
+       }
+
+       /**
+        * Resets the SiteList
+        *
+        * @since 1.25
+        */
+       public function reset() {
+               $this->sites = null;
+       }
+
+       /**
+        * Clears the list of sites stored in the database.
+        *
+        * @see SiteStore::clear()
+        *
+        * @return bool Success
+        */
+       public function clear() {
+               $dbw = $this->sitesTable->getWriteDbConnection();
+
+               $dbw->startAtomic( __METHOD__ );
+               $ok = $dbw->delete( 'sites', '*', __METHOD__ );
+               $ok = $dbw->delete( 'site_identifiers', '*', __METHOD__ ) && $ok;
+               $dbw->endAtomic( __METHOD__ );
+
+               $this->reset();
+
+               return $ok;
+       }
+
+       /**
+        * @since 1.25
+        *
+        * @return ORMTable
+        */
+       protected function newSitesTable() {
+               return new ORMTable(
+                       'sites',
+                       array(
+                               'id' => 'id',
+
+                               // Site data
+                               'global_key' => 'str',
+                               'type' => 'str',
+                               'group' => 'str',
+                               'source' => 'str',
+                               'language' => 'str',
+                               'protocol' => 'str',
+                               'domain' => 'str',
+                               'data' => 'array',
+
+                               // Site config
+                               'forward' => 'bool',
+                               'config' => 'array',
+                       ),
+                       array(
+                               'type' => Site::TYPE_UNKNOWN,
+                               'group' => Site::GROUP_NONE,
+                               'source' => Site::SOURCE_LOCAL,
+                               'data' => array(),
+
+                               'forward' => false,
+                               'config' => array(),
+                               'language' => '',
+                       ),
+                       'ORMRow',
+                       'site_'
+               );
+       }
+
+}
diff --git a/includes/site/FileBasedSiteLookup.php b/includes/site/FileBasedSiteLookup.php
new file mode 100644 (file)
index 0000000..9654440
--- /dev/null
@@ -0,0 +1,139 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ *
+ * @license GNU GPL v2+
+ */
+
+/**
+ * Provides a file-based cache of a SiteStore. The sites are stored in
+ * a json file. (see docs/sitescache.txt regarding format)
+ *
+ * The cache can be built with the rebuildSitesCache.php maintenance script,
+ * and a MediaWiki instance can be setup to use this by setting the
+ * 'wgSitesCacheFile' configuration to the cache file location.
+ *
+ * @since 1.25
+ */
+class FileBasedSiteLookup implements SiteLookup {
+
+       /**
+        * @var SiteList
+        */
+       private $sites = null;
+
+       /**
+        * @var string
+        */
+       private $cacheFile;
+
+       /**
+        * @param string $cacheFile
+        */
+       public function __construct( $cacheFile ) {
+               $this->cacheFile = $cacheFile;
+       }
+
+       /**
+        * @since 1.25
+        *
+        * @return SiteList
+        */
+       public function getSites() {
+               if ( $this->sites === null ) {
+                       $this->sites = $this->loadSitesFromCache();
+               }
+
+               return $this->sites;
+       }
+
+       /**
+        * @param string $globalId
+        *
+        * @since 1.25
+        *
+        * @return Site|null
+        */
+       public function getSite( $globalId ) {
+               $sites = $this->getSites();
+
+               return $sites->hasSite( $globalId ) ? $sites->getSite( $globalId ) : null;
+       }
+
+       /**
+        * @return SiteList
+        */
+       private function loadSitesFromCache() {
+               $data = $this->loadJsonFile();
+
+               $sites = new SiteList();
+
+               // @todo lazy initialize the site objects in the site list (e.g. only when needed to access)
+               foreach ( $data['sites'] as $siteArray ) {
+                       $sites[] = $this->newSiteFromArray( $siteArray );
+               }
+
+               return $sites;
+       }
+
+       /**
+        * @throws MWException
+        * @return array see docs/sitescache.txt for format of the array.
+        */
+       private function loadJsonFile() {
+               if ( !is_readable( $this->cacheFile ) ) {
+                       throw new MWException( 'SiteList cache file not found.' );
+               }
+
+               $contents = file_get_contents( $this->cacheFile );
+               $data = json_decode( $contents, true );
+
+               if ( !is_array( $data ) || !is_array( $data['sites'] )
+                       || !array_key_exists( 'sites', $data )
+               ) {
+                       throw new MWException( 'SiteStore json cache data is invalid.' );
+               }
+
+               return $data;
+       }
+
+       /**
+        * @param array $data
+        *
+        * @return Site
+        */
+       private function newSiteFromArray( array $data ) {
+               $siteType = array_key_exists( 'type', $data ) ? $data['type'] : Site::TYPE_UNKNOWN;
+               $site = Site::newForType( $siteType );
+
+               $site->setGlobalId( $data['globalid'] );
+               $site->setForward( $data['forward'] );
+               $site->setGroup( $data['group'] );
+               $site->setLanguageCode( $data['language'] );
+               $site->setSource( $data['source'] );
+               $site->setExtraData( $data['data'] );
+               $site->setExtraConfig( $data['config'] );
+
+               foreach ( $data['identifiers'] as $identifier ) {
+                       $site->addLocalId( $identifier['type'], $identifier['key'] );
+               }
+
+               return $site;
+       }
+
+}
index 38528d6..2c25472 100644 (file)
@@ -54,6 +54,8 @@ class HashSiteStore implements SiteStore {
         */
        public function saveSite( Site $site ) {
                $this->sites[$site->getGlobalId()] = $site;
+
+               return true;
        }
 
        /**
@@ -69,6 +71,8 @@ class HashSiteStore implements SiteStore {
                foreach ( $sites as $site ) {
                        $this->saveSite( $site );
                }
+
+               return true;
        }
 
        /**
@@ -112,6 +116,8 @@ class HashSiteStore implements SiteStore {
         */
        public function clear() {
                $this->sites = array();
+
+               return true;
        }
 
 }
diff --git a/includes/site/SiteListFileCache.php b/includes/site/SiteListFileCache.php
deleted file mode 100644 (file)
index e48a187..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-<?php
-/**
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- *
- * @license GNU GPL v2+
- */
-
-/**
- * Provides a file-based cache of a SiteStore, stored as a json file.
- * The cache can be built with the rebuildSitesCache.php maintenance script,
- * and a MediaWiki instance can be setup to use this by setting the
- * 'wgSitesCacheFile' configuration to the cache file location.
- *
- * @since 1.25
- */
-class SiteListFileCache {
-
-       /**
-        * @var SiteList
-        */
-       private $sites = null;
-
-       /**
-        * @var string
-        */
-       private $cacheFile;
-
-       /**
-        * @param string $cacheFile
-        */
-       public function __construct( $cacheFile ) {
-               $this->cacheFile = $cacheFile;
-       }
-
-       /**
-        * @since 1.25
-        *
-        * @return SiteList
-        */
-       public function getSites() {
-               if ( $this->sites === null ) {
-                       $this->sites = $this->loadSitesFromCache();
-               }
-
-               return $this->sites;
-       }
-
-       /**
-        * @param string $globalId
-        *
-        * @since 1.25
-        *
-        * @return Site|null
-        */
-       public function getSite( $globalId ) {
-               $sites = $this->getSites();
-
-               return $sites->hasSite( $globalId ) ? $sites->getSite( $globalId ) : null;
-       }
-
-       /**
-        * @return SiteList
-        */
-       private function loadSitesFromCache() {
-               $data = $this->loadJsonFile();
-
-               $sites = new SiteList();
-
-               // @todo lazy initialize the site objects in the site list (e.g. only when needed to access)
-               foreach ( $data['sites'] as $siteArray ) {
-                       $sites[] = $this->newSiteFromArray( $siteArray );
-               }
-
-               return $sites;
-       }
-
-       /**
-        * @throws MWException
-        * @return array
-        */
-       private function loadJsonFile() {
-               if ( !is_readable( $this->cacheFile ) ) {
-                       throw new MWException( 'SiteList cache file not found.' );
-               }
-
-               $contents = file_get_contents( $this->cacheFile );
-               $data = json_decode( $contents, true );
-
-               if ( !is_array( $data ) || !array_key_exists( 'sites', $data ) ) {
-                       throw new MWException( 'SiteStore json cache data is invalid.' );
-               }
-
-               return $data;
-       }
-
-       /**
-        * @param array $data
-        *
-        * @return Site
-        */
-       private function newSiteFromArray( array $data ) {
-               $siteType = array_key_exists( 'type', $data ) ? $data['type'] : Site::TYPE_UNKNOWN;
-               $site = Site::newForType( $siteType );
-
-               $site->setGlobalId( $data['globalid'] );
-               $site->setInternalId( $data['internalid'] );
-               $site->setForward( $data['forward'] );
-               $site->setGroup( $data['group'] );
-               $site->setLanguageCode( $data['language'] );
-               $site->setSource( $data['source'] );
-               $site->setExtraData( $data['data'] );
-               $site->setExtraConfig( $data['config'] );
-
-               foreach ( $data['identifiers'] as $identifier ) {
-                       $site->addLocalId( $identifier['type'], $identifier['key'] );
-               }
-
-               return $site;
-       }
-
-}
diff --git a/includes/site/SiteListFileCacheBuilder.php b/includes/site/SiteListFileCacheBuilder.php
deleted file mode 100644 (file)
index 7307a6f..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-<?php
-
-/**
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @since 1.25
- *
- * @file
- *
- * @license GNU GPL v2+
- */
-class SiteListFileCacheBuilder {
-
-       /**
-        * @var SiteStore
-        */
-       private $siteStore;
-
-       /**
-        * @var string
-        */
-       private $cacheFile;
-
-       /**
-        * @param SiteStore $siteStore
-        * @param string $cacheFile
-        */
-       public function __construct( SiteStore $siteStore, $cacheFile ) {
-               $this->siteStore = $siteStore;
-               $this->cacheFile = $cacheFile;
-       }
-
-       public function build() {
-               $this->sites = $this->siteStore->getSites( 'recache' );
-               $this->cacheSites( $this->sites->getArrayCopy() );
-       }
-
-       /**
-        * @param Site[] $sites
-        *
-        * @throws MWException if in manualRecache mode
-        * @return bool
-        */
-       private function cacheSites( array $sites ) {
-               $sitesArray = array();
-
-               foreach ( $sites as $site ) {
-                       $globalId = $site->getGlobalId();
-                       $sitesArray[$globalId] = $this->getSiteAsArray( $site );
-               }
-
-               $json = json_encode( array(
-                       'sites' => $sitesArray
-               ) );
-
-               $result = file_put_contents( $this->cacheFile, $json );
-
-               return $result !== false;
-       }
-
-       /**
-        * @param Site $site
-        *
-        * @return array
-        */
-       private function getSiteAsArray( Site $site ) {
-               $siteEntry = unserialize( $site->serialize() );
-               $siteIdentifiers = $this->buildLocalIdentifiers( $site );
-               $identifiersArray = array();
-
-               foreach ( $siteIdentifiers as $identifier ) {
-                       $identifiersArray[] = $identifier;
-               }
-
-               $siteEntry['identifiers'] = $identifiersArray;
-
-               return $siteEntry;
-       }
-
-       /**
-        * @param Site $site
-        *
-        * @return array Site local identifiers
-        */
-       private function buildLocalIdentifiers( Site $site ) {
-               $localIds = array();
-
-               foreach ( $site->getLocalIds() as $idType => $ids ) {
-                       foreach ( $ids as $id ) {
-                               $localIds[] = array(
-                                       'type' => $idType,
-                                       'key' => $id
-                               );
-                       }
-               }
-
-               return $localIds;
-       }
-
-}
diff --git a/includes/site/SiteLookup.php b/includes/site/SiteLookup.php
new file mode 100644 (file)
index 0000000..610bf0b
--- /dev/null
@@ -0,0 +1,50 @@
+<?php
+
+/**
+ * Interface for service objects providing a lookup of Site objects.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @since 1.25
+ *
+ * @file
+ * @ingroup Site
+ *
+ * @license GNU GPL v2+
+ */
+interface SiteLookup {
+
+       /**
+        * Returns the site with provided global id, or null if there is no such site.
+        *
+        * @since 1.25
+        *
+        * @param string $globalId
+        *
+        * @return Site|null
+        */
+       public function getSite( $globalId );
+
+       /**
+        * Returns a list of all sites.
+        *
+        * @since 1.25
+        *
+        * @return SiteList
+        */
+       public function getSites();
+
+}
index 8f25c76..d77f07b 100644 (file)
  * @license GNU GPL v2+
  * @author Jeroen De Dauw < jeroendedauw@gmail.com >
  */
-class SiteSQLStore implements SiteStore {
-       /**
-        * @since 1.21
-        *
-        * @var SiteList|null
-        */
-       protected $sites = null;
-
-       /**
-        * @var ORMTable
-        */
-       protected $sitesTable;
-
-       /**
-        * @var string|null
-        */
-       private $cacheKey = null;
-
-       /**
-        * @var int
-        */
-       private $cacheTimeout = 3600;
-
-       /**
-        * @var BagOStuff
-        */
-       private $cache;
+class SiteSQLStore extends CachingSiteStore {
 
        /**
         * @since 1.21
+        * @deprecated 1.25 Construct a SiteStore instance directly instead.
         *
         * @param ORMTable|null $sitesTable
         * @param BagOStuff|null $cache
@@ -69,370 +44,9 @@ class SiteSQLStore implements SiteStore {
                        $cache = wfGetMainCache();
                }
 
-               return new static( $cache, $sitesTable );
-       }
-
-       /**
-        * Constructor.
-        *
-        * @since 1.21
-        *
-        * @param BagOStuff $cache
-        * @param ORMTable|null $sitesTable
-        */
-       protected function __construct( BagOStuff $cache, ORMTable $sitesTable = null ) {
-               if ( $sitesTable === null ) {
-                       $sitesTable = $this->newSitesTable();
-               }
-
-               $this->cache = $cache;
-               $this->sitesTable = $sitesTable;
-       }
-
-       /**
-        * Constructs a cache key to use for caching the list of sites.
-        *
-        * This includes the concrete class name of the site list as well as a version identifier
-        * for the list's serialization, to avoid problems when unserializing site lists serialized
-        * by an older version, e.g. when reading from a cache.
-        *
-        * The cache key also includes information about where the sites were loaded from, e.g.
-        * the name of a database table.
-        *
-        * @see SiteList::getSerialVersionId
-        *
-        * @return string The cache key.
-        */
-       protected function getCacheKey() {
-
-               if ( $this->cacheKey === null ) {
-                       $type = 'SiteList#' . SiteList::getSerialVersionId();
-                       $source = $this->sitesTable->getName();
-
-                       if ( $this->sitesTable->getTargetWiki() !== false ) {
-                               $source = $this->sitesTable->getTargetWiki() . '.' . $source;
-                       }
-
-                       $this->cacheKey = wfMemcKey( "$source/$type" );
-               }
-
-               return $this->cacheKey;
-       }
-
-       /**
-        * @see SiteStore::getSites
-        *
-        * @since 1.21
-        *
-        * @param string $source Either 'cache' or 'recache'
-        *
-        * @return SiteList
-        */
-       public function getSites( $source = 'cache' ) {
-
-               if ( $source === 'cache' ) {
-                       if ( $this->sites === null ) {
-                               $sites = $this->cache->get( $this->getCacheKey() );
-
-                               if ( is_object( $sites ) ) {
-                                       $this->sites = $sites;
-                               } else {
-                                       $this->loadSites();
-                               }
-                       }
-               }
-               else {
-                       $this->loadSites();
-               }
-
-               return $this->sites;
-       }
-
-       /**
-        * Returns a new Site object constructed from the provided ORMRow.
-        *
-        * @since 1.21
-        *
-        * @param ORMRow $siteRow
-        *
-        * @return Site
-        */
-       protected function siteFromRow( ORMRow $siteRow ) {
-
-               $site = Site::newForType( $siteRow->getField( 'type', Site::TYPE_UNKNOWN ) );
-
-               $site->setGlobalId( $siteRow->getField( 'global_key' ) );
-
-               $site->setInternalId( $siteRow->getField( 'id' ) );
-
-               if ( $siteRow->hasField( 'forward' ) ) {
-                       $site->setForward( $siteRow->getField( 'forward' ) );
-               }
-
-               if ( $siteRow->hasField( 'group' ) ) {
-                       $site->setGroup( $siteRow->getField( 'group' ) );
-               }
-
-               if ( $siteRow->hasField( 'language' ) ) {
-                       $site->setLanguageCode( $siteRow->getField( 'language' ) === ''
-                               ? null
-                               : $siteRow->getField( 'language' )
-                       );
-               }
-
-               if ( $siteRow->hasField( 'source' ) ) {
-                       $site->setSource( $siteRow->getField( 'source' ) );
-               }
-
-               if ( $siteRow->hasField( 'data' ) ) {
-                       $site->setExtraData( $siteRow->getField( 'data' ) );
-               }
-
-               if ( $siteRow->hasField( 'config' ) ) {
-                       $site->setExtraConfig( $siteRow->getField( 'config' ) );
-               }
-
-               return $site;
-       }
-
-       /**
-        * Get a new ORMRow from a Site object
-        *
-        * @since 1.22
-        *
-        * @param Site $site
-        *
-        * @return ORMRow
-        */
-       protected function getRowFromSite( Site $site ) {
-               $fields = array(
-                       // Site data
-                       'global_key' => $site->getGlobalId(), // TODO: check not null
-                       'type' => $site->getType(),
-                       'group' => $site->getGroup(),
-                       'source' => $site->getSource(),
-                       'language' => $site->getLanguageCode() === null ? '' : $site->getLanguageCode(),
-                       'protocol' => $site->getProtocol(),
-                       'domain' => strrev( $site->getDomain() ) . '.',
-                       'data' => $site->getExtraData(),
-
-                       // Site config
-                       'forward' => $site->shouldForward(),
-                       'config' => $site->getExtraConfig(),
-               );
-
-               if ( $site->getInternalId() !== null ) {
-                       $fields['id'] = $site->getInternalId();
-               }
-
-               return new ORMRow( $this->sitesTable, $fields );
-       }
-
-       /**
-        * Fetches the site from the database and loads them into the sites field.
-        *
-        * @since 1.21
-        */
-       protected function loadSites() {
-
-               $this->sites = new SiteList();
-
-               foreach ( $this->sitesTable->select() as $siteRow ) {
-                       $this->sites[] = $this->siteFromRow( $siteRow );
-               }
-
-               // Batch load the local site identifiers.
-               $ids = wfGetDB( $this->sitesTable->getReadDb() )->select(
-                       'site_identifiers',
-                       array(
-                               'si_site',
-                               'si_type',
-                               'si_key',
-                       ),
-                       array(),
-                       __METHOD__
-               );
-
-               foreach ( $ids as $id ) {
-                       if ( $this->sites->hasInternalId( $id->si_site ) ) {
-                               $site = $this->sites->getSiteByInternalId( $id->si_site );
-                               $site->addLocalId( $id->si_type, $id->si_key );
-                               $this->sites->setSite( $site );
-                       }
-               }
-
-               $this->cache->set( $this->getCacheKey(), $this->sites, $this->cacheTimeout );
-
-       }
-
-       /**
-        * @see SiteStore::getSite
-        *
-        * @since 1.21
-        *
-        * @param string $globalId
-        * @param string $source
-        *
-        * @return Site|null
-        */
-       public function getSite( $globalId, $source = 'cache' ) {
-
-               $sites = $this->getSites( $source );
-
-               return $sites->hasSite( $globalId ) ? $sites->getSite( $globalId ) : null;
-       }
-
-       /**
-        * @see SiteStore::saveSite
-        *
-        * @since 1.21
-        *
-        * @param Site $site
-        *
-        * @return bool Success indicator
-        */
-       public function saveSite( Site $site ) {
-               return $this->saveSites( array( $site ) );
-       }
-
-       /**
-        * @see SiteStore::saveSites
-        *
-        * @since 1.21
-        *
-        * @param Site[] $sites
-        *
-        * @return bool Success indicator
-        */
-       public function saveSites( array $sites ) {
-
-               if ( empty( $sites ) ) {
-                       return true;
-               }
-
-               $dbw = $this->sitesTable->getWriteDbConnection();
-
-               $dbw->startAtomic( __METHOD__ );
-
-               $success = true;
-
-               $internalIds = array();
-               $localIds = array();
-
-               foreach ( $sites as $site ) {
-                       if ( $site->getInternalId() !== null ) {
-                               $internalIds[] = $site->getInternalId();
-                       }
-
-                       $siteRow = $this->getRowFromSite( $site );
-                       $success = $siteRow->save( __METHOD__ ) && $success;
-
-                       foreach ( $site->getLocalIds() as $idType => $ids ) {
-                               foreach ( $ids as $id ) {
-                                       $localIds[] = array( $siteRow->getId(), $idType, $id );
-                               }
-                       }
-               }
-
-               if ( $internalIds !== array() ) {
-                       $dbw->delete(
-                               'site_identifiers',
-                               array( 'si_site' => $internalIds ),
-                               __METHOD__
-                       );
-               }
-
-               foreach ( $localIds as $localId ) {
-                       $dbw->insert(
-                               'site_identifiers',
-                               array(
-                                       'si_site' => $localId[0],
-                                       'si_type' => $localId[1],
-                                       'si_key' => $localId[2],
-                               ),
-                               __METHOD__
-                       );
-               }
-
-               $dbw->endAtomic( __METHOD__ );
-
-               // purge cache
-               $this->reset();
-
-               return $success;
-       }
-
-       /**
-        * Purges the internal and external cache of the site list, forcing the list
-        * of sites to be re-read from the database.
-        *
-        * @since 1.21
-        */
-       public function reset() {
-               // purge cache
-               $this->cache->delete( $this->getCacheKey() );
-               $this->sites = null;
-
-       }
-
-       /**
-        * Clears the list of sites stored in the database.
-        *
-        * @see SiteStore::clear()
-        *
-        * @return bool Success
-        */
-       public function clear() {
-               $dbw = $this->sitesTable->getWriteDbConnection();
-
-               $dbw->startAtomic( __METHOD__ );
-               $ok = $dbw->delete( 'sites', '*', __METHOD__ );
-               $ok = $dbw->delete( 'site_identifiers', '*', __METHOD__ ) && $ok;
-               $dbw->endAtomic( __METHOD__ );
-
-               $this->reset();
-
-               return $ok;
-       }
-
-       /**
-        * @since 1.21
-        *
-        * @return ORMTable
-        */
-       protected function newSitesTable() {
-               return new ORMTable(
-                       'sites',
-                       array(
-                               'id' => 'id',
-
-                               // Site data
-                               'global_key' => 'str',
-                               'type' => 'str',
-                               'group' => 'str',
-                               'source' => 'str',
-                               'language' => 'str',
-                               'protocol' => 'str',
-                               'domain' => 'str',
-                               'data' => 'array',
-
-                               // Site config
-                               'forward' => 'bool',
-                               'config' => 'array',
-                       ),
-                       array(
-                               'type' => Site::TYPE_UNKNOWN,
-                               'group' => Site::GROUP_NONE,
-                               'source' => Site::SOURCE_LOCAL,
-                               'data' => array(),
+               $siteStore = new DBSiteStore();
 
-                               'forward' => false,
-                               'config' => array(),
-                               'language' => '',
-                       ),
-                       'ORMRow',
-                       'site_'
-               );
+               return new static( $siteStore, $cache );
        }
 
 }
index 537f1cc..10e0c1b 100644 (file)
@@ -26,7 +26,7 @@
  * @license GNU GPL v2+
  * @author Jeroen De Dauw < jeroendedauw@gmail.com >
  */
-interface SiteStore {
+interface SiteStore extends SiteLookup {
 
        /**
         * Saves the provided site.
@@ -50,33 +50,6 @@ interface SiteStore {
         */
        public function saveSites( array $sites );
 
-       /**
-        * Returns the site with provided global id, or null if there is no such site.
-        *
-        * @since 1.21
-        *
-        * @param string $globalId
-        * @param string $source Either 'cache' or 'recache'.
-        * If 'cache', the values are allowed (but not obliged) to come from a cache.
-        *
-        * @return Site|null
-        */
-       public function getSite( $globalId, $source = 'cache' );
-
-       /**
-        * Returns a list of all sites. By default this site is
-        * fetched from the cache, which can be changed to loading
-        * the list from the database using the $useCache parameter.
-        *
-        * @since 1.21
-        *
-        * @param string $source Either 'cache' or 'recache'.
-        * If 'cache', the values are allowed (but not obliged) to come from a cache.
-        *
-        * @return SiteList
-        */
-       public function getSites( $source = 'cache' );
-
        /**
         * Deletes all sites from the database. After calling clear(), getSites() will return an empty
         * list and getSite() will return null until saveSite() or saveSites() is called.
diff --git a/includes/site/SitesCacheFileBuilder.php b/includes/site/SitesCacheFileBuilder.php
new file mode 100644 (file)
index 0000000..2e42040
--- /dev/null
@@ -0,0 +1,113 @@
+<?php
+
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @since 1.25
+ *
+ * @file
+ *
+ * @license GNU GPL v2+
+ */
+class SitesCacheFileBuilder {
+
+       /**
+        * @var SiteLookup
+        */
+       private $siteLookup;
+
+       /**
+        * @var string
+        */
+       private $cacheFile;
+
+       /**
+        * @param SiteLookup $siteLookup
+        * @param string $cacheFile
+        */
+       public function __construct( SiteLookup $siteLookup, $cacheFile ) {
+               $this->siteLookup = $siteLookup;
+               $this->cacheFile = $cacheFile;
+       }
+
+       public function build() {
+               $this->sites = $this->siteLookup->getSites();
+               $this->cacheSites( $this->sites->getArrayCopy() );
+       }
+
+       /**
+        * @param Site[] $sites
+        *
+        * @throws MWException if in manualRecache mode
+        * @return bool
+        */
+       private function cacheSites( array $sites ) {
+               $sitesArray = array();
+
+               foreach ( $sites as $site ) {
+                       $globalId = $site->getGlobalId();
+                       $sitesArray[$globalId] = $this->getSiteAsArray( $site );
+               }
+
+               $json = json_encode( array(
+                       'sites' => $sitesArray
+               ) );
+
+               $result = file_put_contents( $this->cacheFile, $json );
+
+               return $result !== false;
+       }
+
+       /**
+        * @param Site $site
+        *
+        * @return array
+        */
+       private function getSiteAsArray( Site $site ) {
+               $siteEntry = unserialize( $site->serialize() );
+               $siteIdentifiers = $this->buildLocalIdentifiers( $site );
+               $identifiersArray = array();
+
+               foreach ( $siteIdentifiers as $identifier ) {
+                       $identifiersArray[] = $identifier;
+               }
+
+               $siteEntry['identifiers'] = $identifiersArray;
+
+               return $siteEntry;
+       }
+
+       /**
+        * @param Site $site
+        *
+        * @return array Site local identifiers
+        */
+       private function buildLocalIdentifiers( Site $site ) {
+               $localIds = array();
+
+               foreach ( $site->getLocalIds() as $idType => $ids ) {
+                       foreach ( $ids as $id ) {
+                               $localIds[] = array(
+                                       'type' => $idType,
+                                       'key' => $id
+                               );
+                       }
+               }
+
+               return $localIds;
+       }
+
+}
index 21e465a..9056761 100644 (file)
@@ -89,11 +89,9 @@ abstract class FormSpecialPage extends SpecialPage {
         * @return HTMLForm|null
         */
        protected function getForm() {
-               $this->fields = $this->getFormFields();
-
                $form = HTMLForm::factory(
                        $this->getDisplayFormat(),
-                       $this->fields,
+                       $this->getFormFields(),
                        $this->getContext(),
                        $this->getMessagePrefix()
                );
index ff263b6..0b8147e 100644 (file)
@@ -114,6 +114,12 @@ class SpecialTags extends SpecialPage {
                $showActions = $user->isAllowed( 'managechangetags' );
 
                // Write the headers
+               $tagUsageStatistics = ChangeTags::tagUsageStatistics();
+
+               // Show header only if there exists atleast one tag
+               if ( !$tagUsageStatistics ) {
+                       return;
+               }
                $html = Xml::tags( 'tr', null, Xml::tags( 'th', null, $this->msg( 'tags-tag' )->parse() ) .
                        Xml::tags( 'th', null, $this->msg( 'tags-display-header' )->parse() ) .
                        Xml::tags( 'th', null, $this->msg( 'tags-description-header' )->parse() ) .
@@ -134,7 +140,7 @@ class SpecialTags extends SpecialPage {
                $this->extensionActivatedTags = array_fill_keys(
                        ChangeTags::listExtensionActivatedTags(), true );
 
-               foreach ( ChangeTags::tagUsageStatistics() as $tag => $hitcount ) {
+               foreach ( $tagUsageStatistics as $tag => $hitcount ) {
                        $html .= $this->doTagRow( $tag, $hitcount, $showActions );
                }
 
index ed89ccf..82bfc31 100644 (file)
        "viewyourtext": "تنجم تشوف و تنسّخ المصدر تاع <strong>التبدالات تاعك</strong> ف هاد الصفحة:",
        "protectedinterface": "هاد الباجة توفّر لك وريشة (interface) تاع كتابة مديورة لل صوفتوار الّي ف هاد الويكي، و راهي مأمّنة باش تنحضا من الضرارات.\nباش تزيد ولا تبدّل طرجمات ل كامل الويكيّات، من فضلك استعمل [//translatewiki.net/ translatewiki.net] المشروع ميدياويكي تاع التبلادات.",
        "yourname": "اسم المستخدم:",
+       "userlogin-yourname": "سميّة تاع المستعملي",
+       "userlogin-yourname-ph": "دخّل السميّة تاع المستعملي تاعك",
        "yourpassword": "كلمة السر:",
+       "userlogin-yourpassword": "كلمت` السرّ",
+       "userlogin-yourpassword-ph": "دخّل كلمت` السرّ تاعك",
+       "createacct-yourpassword-ph": "دخّل كلمت` سرّ",
        "yourpasswordagain": "عاود كتبت كلمت السر:",
+       "createacct-yourpasswordagain": "أكّد كلمت` السرّ",
+       "createacct-yourpasswordagain-ph": "عاود دخّل كلمت` السرّ",
        "remembermypassword": "اتفكر الدخول تاعي ب هاذ النافيكاتور (ب مدّة حدها{{PLURAL:$1||يوم واحد|يومين|$1 إيّام|$1 يوم}})",
+       "userlogin-remembermypassword": "خلّيني مسجّل داخل",
        "login": "كونكسيون",
        "nav-login-createaccount": "تسجل/ اصنع حساب",
        "userlogin": "تسجل/ اصنع حساب",
        "userlogout": "سجل خروج",
+       "userlogin-noaccount": "ما عندك حساب؟",
+       "userlogin-joinproject": "انضم لـ {{SITENAME}}",
        "nologin": "ما عندكش حساب مسجل؟ '''$1'''.",
        "nologinlink": "اصنع حساب",
        "createaccount": "اصنع حساب",
        "gotaccount": "عندك حساب مسجل؟ '''$1'''.",
        "gotaccountlink": "كونكسيون",
        "userlogin-resetlink": "نسيت تفاصيل الدخول؟",
+       "userlogin-resetpassword-link": "راك ناسي كلمت` السرّ؟",
+       "userlogin-helplink2": "معاونة ف تسجال الدخول",
+       "createacct-emailoptional": "علوان ليمال (ماشي مسيّف)",
+       "createacct-email-ph": "دخّل علوان` ليمال تاعك",
+       "createacct-captcha": "تحقق أمني",
+       "createacct-imgcaptcha-ph": "دخّل النصّ الّي راك تشوفهُ لفوق",
+       "createacct-submit": "اصنع حسابك",
+       "createacct-benefit-heading": "{{SITENAME}} مخلوق من عند شي ناس غير كيفك.",
+       "createacct-benefit-body1": "{{PLURAL:$1|تحرير|تحريرات}}",
+       "createacct-benefit-body2": "$1 {{PLURAL:$1|صفحة|صفحات}}",
+       "createacct-benefit-body3": "{{PLURAL:$1|مساهم|مساهمين}} توالا",
        "mailmypassword": "أرسل لي كلمة سر جديده",
        "loginlanguagelabel": "اللوغه: $1",
        "pt-login": "دخله",
+       "pt-login-button": "دخول",
        "pt-createaccount": "اصنع حساب",
+       "pt-userlogout": "خروج",
+       "passwordreset": "صفّي كلمت` السرّ",
        "bold_sample": "كتيبة غليظه",
        "bold_tip": "كتيبة غليظه",
        "italic_sample": "كتبة مايلة",
        "preview": "شوفه-قبلي",
        "showpreview": "بين معاينة",
        "showdiff": "عرض التبديلات",
-       "anoneditwarning": "'''تÙ\88Ù\84Ù\87:''' Ø±Ø§Ù\83 Ù\85ا Ø¯Ø®Ù\84تش.\nغادÙ\8a Ù\8aتسجÙ\84 Ù\84ادراس Ø¢Ù\8aبÙ\8a Ù\86تاعÙ\83 Ù\81Ù\8a ØªØ§Ø±Ù\8aØ® Ù\87Ø°Ù\87 Ø§Ù\84باجÙ\87.",
+       "anoneditwarning": "'''تÙ\88Ù\84Ù\8aÙ\87Ø©:''' Ø±Ø§Ù\83 Ù\85ا Ø¯Ø®Ù\84تش Ø¨Ù\84 Ø­Ø³Ø§Ø¨ ØªØ§Ø¹Ù\83.\nÙ\8aÙ\84ا ØªØ¯Ù\8aر Ø´Ù\8a ØªØ¨Ø¯Ø§Ù\84Ø\8c ØºØ§Ø¯Ù\8a ØªØªØ³Ø¬Ù\91Ù\84 Ù\84ادرÙ\8aسة Ø¢Ù\8aبÙ\8a ØªØ§Ø¹Ù\83 Ù\81Ù\84 Ù\85تراخ ØªØ§Ø¹ Ù\87اد Ø§Ù\84صÙ\81حة Ù\88 ØªÙ\83Ù\88Ù\86 Ø¨Ø§Ù\8aÙ\86Ø© Ù\84 Ù\83Ù\84Ù\91 Ù\88احد. Ù\8aÙ\84ا [$1 ØªØªÙ\83Ù\88Ù\86Ù\8aÙ\83تا]</strong> Ù\88Ù\84ا <strong>[$2 ØªØ®Ù\84Ù\82 Ø­Ø³Ø§Ø¨]</strong>Ø\8c Ø§Ù\84تبداÙ\84ات ØªØ§Ø¹Ù\83 ØºØ§Ø¯Ù\8a Ù\8aباÙ\86Ù\88 ØªØ­Øª Ø§Ù\84سÙ\85Ù\8aÙ\91Ø© ØªØ§Ø¹ Ø§Ù\84Ù\85ستعÙ\85Ù\84Ù\8a ØªØ§Ø¹Ù\83Ø\8c Ù\88 Ù\83اÙ\8aÙ\86 ØªØ§Ù\86Ù\8a Ù\85زÙ\8aÙ\91ات Ù\88حدخرÙ\8aÙ\86.",
        "loginreqlink": "اتكونيكتا",
        "newarticle": "(جديد)",
        "newarticletext": "راك تبعت وصيلة لباجه لم ما تخدمتش بعد.\nباش تصنع هاذ الباجه ابدا الكتبه فالصندوق التحت (شوف في [$1  زياده باجه المساعده] لمعلومات).\nإذا كانت زيارتك لهاذ الباجه غلطه، ادرك على بوطون''ولى'' في نافيقاتور الإنترنت نتاعك.",
        "noarticletext-nopermission": "لحد الساعه ما كانش حتى نص في هاذ الباجه.\nتقدرو [[Special:Search/{{PAGENAME}}|ترميو تفتيشه على هاذ العنوان]] فالباجات لخرين,\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} فتش فالعمليات المربوطه]\nو إلا[{{fullurl:{{FULLPAGENAME}}|action=edit}} أصنع هاذ الباجه]</span>.",
        "previewnote": "'''ما تنساش هذي معاينه قبليه.'''\nلدوك التبديلات دياولك ما تسجلوش!",
        "editing": "تحرير $1",
+       "creating": "خليق $1",
        "editingsection": "كتيبه $1 (قسم)",
        "templatesused": "{{PLURAL:$1||القالب المستعمل|القوالب المستعمله}} في هذه الباجه:",
        "template-protected": "(محميه)",
        "revdel-restore": "غير كيف راهي تبان",
        "revertmerge": "فرق",
        "history-title": " «$1»: تاريخ المراجعات",
+       "difference-title": "فروقات بين التبدالات تاع \"$1\"",
        "lineno": "سطر$1:",
        "compareselectedversions": "كومباري بين نسختين مخيرين",
        "editundo": "نحي",
        "shown-title": "آفيشي $1 {{PLURAL:$1|نتيجة}} فل صفحة",
        "viewprevnext": "شوف($1 {{int:pipe-separator}} $2) ($3).",
        "searchmenu-exists": "'''كاين باجه اسمها « [[:$1]] » في هاذ الويكي'''",
-       "searchmenu-new": "'''أصنع الباجه « [[:$1|$1]] » في هذ الويكي !'''",
+       "searchmenu-new": "'''أصنع الباجة \"[[:$1]]\" في هذ الويكي !'''\n{{PLURAL:$2|0=|شوف تاني الصفحة الّي صبتها ف`التفتاش..|شوف تاني النواتج تاع التفتاش.}}",
        "searchprofile-articles": "باجه تع محتوى",
        "searchprofile-images": "ميلتيميديا",
        "searchprofile-everything": "كلش",
        "search-suggest": "كنت باغي تقول: $1",
        "searchrelated": "مرتابطه",
        "searchall": "ألكل",
+       "search-showingresults": "{{PLURAL:$4|النتيج <strong>$1</strong> تاع <strong>$3</strong>|النتايج <strong>$1 - $2</strong> من أصل <strong>$3</strong>}}",
        "search-nonefound": "ما كانش نتائج تطابق المسقسية.",
        "mypreferences": "إختيارات",
        "youremail": "البريه الالكترونيه:",
        "enhancedrc-history": "تاريخ",
        "recentchanges": "تبديلات توالا",
        "recentchanges-legend": "ابسيون التبديلات التواله",
+       "recentchanges-summary": "اجبر التبدالات اللواخر الّي صراو ف هاد الصفحة.",
        "recentchanges-feed-description": "تبع التبديلات الجدد للويكي في هاذ التيار .",
        "recentchanges-label-newpage": " هاذ التبديل صنع باجه جديده",
        "recentchanges-label-minor": "هاذا تبديل صغير",
        "rcnotefrom": "هاهي التبديلات  من'''$2''' (ل'''$1''' معروضه).",
        "rclistfrom": "بين التبديلات البديه من $3 $2",
        "rcshowhideminor": "$1 التبديلات الصغير",
+       "rcshowhideminor-show": "ورّي",
        "rcshowhideminor-hide": "خبّي",
        "rcshowhidebots": "$1 البوتات",
        "rcshowhidebots-show": "ورّي",
+       "rcshowhidebots-hide": "خبّي",
        "rcshowhideliu": "$1 المستخدمين المسجلين",
        "rcshowhideliu-hide": "خبّي",
        "rcshowhideanons": "$1 المستخدمين المجهولين",
+       "rcshowhideanons-show": "ورّي",
        "rcshowhideanons-hide": "خبّي",
        "rcshowhidepatr": "$1 التبديلات المعسوسه",
        "rcshowhidemine": "$1 تبديلات نتاعي",
+       "rcshowhidemine-show": "ورّي",
        "rcshowhidemine-hide": "خبّي",
        "rclinks": "بين آخر $1 تبديل في آخر $2 يوم<br />$3",
        "diff": "إختلاف",
        "filedesc": "ملخص",
        "license": "ترخيص:",
        "license-header": "ترخيص:",
+       "imgfile": "فيشي",
        "file-anchor-link": "ملف",
        "filehist": "تاريخ الفيشيي",
        "filehist-help": "ادرك على وقت و تاريخ/باش تشوف الملف كما بان  في هاذ الوقت.",
        "pager-older-n": "{{PLURAL:$1|قديم بزاف 1|قديم بزاف $1}}",
        "booksources": "مصادر كتاب",
        "booksources-search-legend": "حوس ما بين مصادر الكتب",
+       "booksources-search": "فتّش",
        "log": "ريجيسترات العمليات",
        "allpages": "قاع الباجات",
        "allarticles": "قاع الباجات",
        "metadata": "بايان ميتا",
        "metadata-help": "هذا الملف راه فيه خبيرات زايدين، بالاك تكون انزادت من عند صواره نيميريك ولا سكانر مين صنع الملف.\nالأصلي، شي تفاصيل بالاك ما تعبرش على الملف المعدل.",
        "metadata-fields": "الحقول تاع الميتا معطيّات تاع تصاور الّي يكونو ف هاد البريّة غادي ينحطّو فل باجة تاع التوصاف تاع التصويرة منين يكون الجدول تاع  الميتاالمعطيات مطوي.\nالحقول لخرة يكونو مخبيين بارديفو.\n* make\n* model\n* datetimeoriginal\n* exposuretime\n* fnumber\n* isospeedratings\n* focallength\n* artist\n* copyright\n* imagedescription\n* gpslatitude\n* gpslongitude\n* gpsaltitude",
+       "exif-orientation": "توجاه",
        "exif-xresolution": "التدقاق الأوفوقي",
        "exif-yresolution": "التدقاق العامودي",
+       "exif-datetime": "تاريخ و وقت تاع تبدال الفيشي",
+       "exif-make": "الصانع تاع الجهاز",
+       "exif-model": "الطبّع تاع الجهاز",
+       "exif-software": "البريمجات الّي مستعملة",
+       "exif-exifversion": "نسخة Exif",
+       "exif-colorspace": "موساع تاع اللوان",
+       "exif-datetimeoriginal": "التاريخ و الوقت تاع تولاد المعلومات",
+       "exif-datetimedigitized": "التاريخ تاع التنمريل (numérisation)",
+       "exif-orientation-1": "عادي",
        "namespacesall": "لكل",
        "monthsall": "لكل",
        "watchlisttools-view": "اعرض التبديلات المرتابطه",
        "watchlisttools-edit": "اعرض قائمه المراقبه و عدلها",
        "watchlisttools-raw": "موديفي ليستت التبيعه الخام",
+       "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|تقرعيج]])",
        "duplicate-defaultsort": "'''توليه:''' مفتاح التستيف الافتراضي \"$2\" ديباسا مفتاح التستيف الافتراضي التالي\"$1\".",
        "specialpages": "الباجات الخصوصيه",
        "external_image_whitelist": " #<pre>خلى هاذ السطر كيما راه\n#حط منثورات التعبيرات المنتظمة (برك الجزء الي يروح بين //) بالتحت\n#هاذ يكون مطابقتها مع مسارات التصاوير البرانيه (الموصولة بصفه مباشره)\n#هاذي الي تشبهغادي تنعرض  كتصاور، خلاف هذا برك وصيلة للتصويرة غادي تنعرض\n#السطور اللي تبدأا ب# تعتبر تعليقات\n#هذا لا يتأثر بحالة الحروف\n\n#حط كامل منثورات التعبيرات المنتظمة فوق هذا السطر. خلي هاذ السطر سواسوا كيما هو</pre>",
        "tag-filter": "صفاية[[Special:Tags|الوشام]]:",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1||وسم|وسمان|وسوم}}]]: $2)",
+       "logentry-delete-delete": "$1 {{GENDER:$2| راه محا|راهي محات}}الصفحة $3",
+       "logentry-move-move": "{{GENDER:$2|نقّل|نقّلت}} $1 الصفحة $3 لـ $4",
        "logentry-newusers-create": "راه تفتح حساب {{GENDER:$2|المستخدم|المستخدمه}} $1",
        "searchsuggest-search": "تفتاش"
 }
index 3e03958..6c7dce0 100644 (file)
        "disclaimers": "अस्वीकरण",
        "disclaimerpage": "Project:साधारण अस्वीकरण",
        "edithelp": "सम्पादन सहायता",
+       "helppage-top-gethelp": "मदद",
        "mainpage": "प्रधान पन्ना",
        "mainpage-description": "प्रधान पन्ना",
        "policy-url": "Project:नीति",
        "import-rootpage-nosubpage": "दिए गए उपसर्ग पृष्ठ \"$1\" के नामस्थान में उप-पृष्ठ नहीं बनाए जा सकते।",
        "importlogpage": "आयात सूची",
        "importlogpagetext": "अन्य विकियों से प्रबन्धकों द्वारा किए गए सम्पादन इतिहास के साथ होने वाले पृष्ठों का आयात।",
-       "import-logentry-upload": "सञ्चिका अपलोड करके [[$1]] का आयात किया",
-       "import-logentry-interwiki": " $1 कय अन्तरविकिकरण कई गय",
        "javascripttest": "जावास्क्रिप्ट परीक्षण",
        "javascripttest-pagetext-unknownframework": "अज्ञात परीक्षण ढाँचा \"$1\"",
        "javascripttest-pagetext-unknownaction": "अज्ञात काम \"$1\".",
        "logentry-move-move": "$1 ने $3 पृष्ठ $4 पर {{GENDER:$2|स्थानांतरित}} कै गय",
        "rightsnone": "(कउनो नाहीं)",
        "revdelete-summary": "संपादन सारांश",
-       "feedback-subject": "विषय:",
-       "feedback-message": "सनेशा:",
-       "feedback-cancel": "रद्द करा जाय",
-       "feedback-submit": "प्रतिक्रिया भेजा जाय",
        "feedback-adding": "पृष्ठ पे प्रतिक्रिया जोडत है ...",
-       "feedback-error2": "त्रुटि: संपादन विफल रहा है",
+       "feedback-cancel": "रद्द करा जाय",
        "feedback-close": "होइ गवा",
+       "feedback-error2": "त्रुटि: संपादन विफल रहा है",
+       "feedback-message": "सनेशा:",
+       "feedback-subject": "विषय:",
+       "feedback-submit": "प्रतिक्रिया भेजा जाय",
        "searchsuggest-search": "खोज",
        "searchsuggest-containing": "...से युक्त",
        "api-error-badaccess-groups": "इ विकि मा आप कय फाइल अपलोड करेक अनुमति नाइ है",
index dc9af98..8bfc90e 100644 (file)
        "rcnotefrom": "Untn san de Endarunga seit  '''$2''' (bis za '''$1''' Ozoagn).",
        "rclistfrom": "Nua Endarunga seit $3 $2 zoagn",
        "rcshowhideminor": "Kloane Endarunga $1",
+       "rcshowhideminor-hide": "Ausblendn",
        "rcshowhidebots": "Bots $1",
        "rcshowhideliu": "Eigloggte Nutza $1",
+       "rcshowhideliu-hide": "Ausblendn",
        "rcshowhideanons": "Anonyme Nutza $1",
+       "rcshowhideanons-hide": "Ausblendn",
        "rcshowhidepatr": "Kontrolliade Endarunga $1",
        "rcshowhidemine": "Meine Beidreg $1",
+       "rcshowhidemine-hide": "Ausblendn",
        "rclinks": "De letztn Endarunga vo de letztn $2 Dog zoagn<br />$3",
        "diff": "Untaschied",
        "hist": "Versiona",
index 87f7764..5713043 100644 (file)
        "disclaimers": "Адмова ад адказнасьці",
        "disclaimerpage": "Project:Адмова ад адказнасьці",
        "edithelp": "Дапамога ў рэдагаваньні",
+       "helppage-top-gethelp": "Дапамога",
        "mainpage": "Галоўная старонка",
        "mainpage-description": "Галоўная старонка",
        "policy-url": "Project:Правілы",
        "tags-hitcount": "$1 {{PLURAL:$1|зьмена|зьмены|зьменаў}}",
        "tags-manage-no-permission": "Вы ня маеце правоў на зьмену метак.",
        "tags-create-heading": "Стварэньне новай меткі",
+       "tags-create-tag-name": "Назва меткі:",
        "comparepages": "Параўнаньне старонак",
        "compare-page1": "Старонка 1",
        "compare-page2": "Старонка 2",
index 27b31f9..f86214f 100644 (file)
        "unknown-error": "یک نا زانتین خطائی رخ دات.",
        "tmp-create-error": "موقتین فایلی جۆڑ کورتین ممکن نه اینت.",
        "tmp-write-error": "خطا بئ موقتین فایلی نیویشتینا.",
+       "windows-nonascii-filename": "ای ویکی پایل ئی ناما گۆ خاسین نۆیسگ ئان پُشتیوانی ئه نه کنت.",
+       "fileexists": "پایل ئی گۆ ای ناما شه دیما موجود اینت، اگه موتمئین {{GENDER:|نه ۆیت}} که لوٹیت ای پایل ئا تغیر ب دهیت، مهربانی بکنیت <strong>[[:$1]]</strong> ئا بگیندیت.\n[[$1|thumb]]",
        "uploadwarning": "بُرز کورتینئ هشدار",
        "savefile": "فایلی ذخیره کورتین",
        "uploaddisabled": "بُرز کورتین غیر پئال اینت.",
        "delete_and_move": "پاک کورتین یا جابیجا",
        "delete_and_move_confirm": "هان،تاکدیم پاک بیئت",
        "delete_and_move_reason": "پاک کورتین  «[[$1]]» جابجایی امکانا",
+       "immobile-source-page": "ای دیم جابیجا ئه نه بیئت.",
        "immobile-target-page": "انتقال ئی امکان په ای ئنوانا موجود نه اینت.",
        "move-leave-redirect": "بئ جا ایشتین یک تغیری مسیر ئی",
        "export": "ڈن کورتین  تاکدیمانئ",
index d101b4b..1184e72 100644 (file)
@@ -31,7 +31,7 @@
        "tog-shownumberswatching": "ध्यान रखे वालन सदस्यन के देखावल जाव",
        "tog-oldsig": "वर्तमान हस्ताक्षर:",
        "tog-fancysig": "हस्ताक्षर के विकी पाठ के रुप में उपयोग करीं (बिना स्वचालित कड़ी के)",
-       "tog-uselivepreview": "लाइव पुर्वालोकन के प्रयोग करीं (प्रयोगात्मक)",
+       "tog-uselivepreview": "लाइव पुर्वालोकन के प्रयोग करीं",
        "tog-forceeditsummary": "यदि सम्पादन सारांश ना दिहल होखे त हमके सूचित करब",
        "tog-watchlisthideown": "हमार ध्यान दिहल पन्ना के सूची से हमरा खातिर परिवर्तन छिपाईं",
        "tog-watchlisthidebots": "हमार ध्यान सूची से बोट द्वारा करल गईल परिवर्तन के छिपाईं",
        "otherlanguages": "अन्य भाषा में",
        "redirectedfrom": "($1 द्वारा पुन: निर्देशित)",
        "redirectpagesub": "पुन: निर्देशित पन्ना",
+       "redirectto": "पुनः निर्देशित:",
        "lastmodifiedat": "$1 के $2 पर ई पन्ना पर अन्तिम बार परिवर्तन भईल।",
        "viewcount": "ई पन्ना {{PLURAL:$1|एक|$1}} बार देखल गईल बा।",
        "protectedpage": "सुरक्षित पन्ना",
        "disclaimers": "अस्विकरण",
        "disclaimerpage": "Project:सामान्य अस्विकरण",
        "edithelp": "मदद सम्पादन",
+       "helppage-top-gethelp": "मदद",
        "mainpage": "मुख्य पन्ना",
        "mainpage-description": "मुख्य पन्ना",
        "policy-url": "Project:नीति",
        "viewsourcetext": "रउआ इ पन्ना के स्त्रोत देख सकत बानी आ एकर नकल उतार सकत बानी:",
        "viewyourtext": "रउआ इ पन्ना में ''आपन सम्पादन'' के स्रोत देख सकत बानी आ ओकर नकल उतार सकत बानी:",
        "protectedinterface": "इ पन्ना इ विकी के सॉफ़्टवेयर के इंटरफ़ेस पाठ्य के देवेला, आ इ के गलत प्रयोग से बचावे खातिर सुरक्षित कर दिहल गइल बा।\nसभन विकियन खातिर अनुवाद जोड़े या बदले खातिर कृपया मीडियाविकि के क्षेत्रीयकरण प्रकल्प [//translatewiki.net/ translatewiki.net] के प्रयोग करीं।",
-       "editinginterface": "'''चेतावनी:''' रउआ एगो अइसन पन्ना के बदल रहल बानी जउन सॉफ़्टवेयर के इंटरफ़ेस पाठ्य प्रदान करेला।\nइ पन्ना के बदलला से अन्य सदस्यन के प्रदर्शित इंटरफ़ेस के शक्ल-सूरत में बदलाव आइ।\nसभे विकियन के खातिर अनुवाद बदले या जोड़े खातिर कृपया मीडियाविकि के क्षेत्रीयकरण परियोजना [//translatewiki.net/wiki/Main_Page?setlang=hi translatewiki.net] के प्रयोग करीं।",
+       "editinginterface": "<strong>चेतावनी:</strong> आप एगो अइसन पन्ना के बदल बदल रहल बानी जवन सॉफ़्टवेयर के इंटरफ़ेस पाठ प्रदान करेला। इ पृष्ठ के बदले से अन्य सदस्यवन के प्रदर्शित इंटरफ़ेस के शक्लोसूरत में बदलाव आई।",
        "cascadeprotected": "इ पन्ना पर सम्पादन करे के अधिकार खत्म कर दिहल गइल बा काहे कि इ निम्न में शामिल बा {{PLURAL:$1|पन्ना, जउन|पन्नां, जउन}} \"व्यापक\" विकल्प के चालू कइला के साथ सुरक्षित कर दिहल गइल बा:\n$2",
        "namespaceprotected": "रउआ के '''$1''' नामस्थान के पन्नं में सम्पादन करे के अधिकार नइखे दिहल गइल।",
        "customcssprotected": "रउआ के इ CSS पन्ना के संपादित करे के अनुमति नइखे, काहे कि इ में अन्य सदस्यं के व्यक्तिगत सेटिंग्स समाविष्ट बा।",
        "invalidtitle-knownnamespace": "\"$2\" नामस्थान आ \"$3\" पाठ्य वाला गलत शीर्षक",
        "invalidtitle-unknownnamespace": "अज्ञात नामस्थान संख्या $1 आ नाम \"$2\" वाला गलत शीर्षक",
        "exception-nologin": "खाता में प्रवेश नईखीं भईल",
-       "exception-nologin-text": "इ पन्ना अथवा कार्य के सक्षम करे खातिर कृपया [[Special:Userlogin|लॉग इन]] करीं।",
+       "exception-nologin-text": "इ पन्ना अथवा कार्य के सक्षम करे खातिर कृपया प्रवेश करीं।",
        "exception-nologin-text-manual": "इ पन्ना अथवा कार्य के सक्षम करे खातिर कृपया $1 करीं।",
        "virus-badscanner": "गलत जमाव: अज्ञात वायरस जाँचक: ''$1''",
        "virus-scanfailed": "जाँच विफल (कोड $1)",
        "post-expand-template-inclusion-category": "अइसन पृष्ठ जे पर साँचा जोडे के सीमा पार हो गइल बा",
        "cantcreateaccounttitle": "खाता खुल नईखे सकत",
        "revisionasof": "$1 के रुप में संशोधन",
-       "revision-info": "$2 में से $1 के रुप में संशोधन",
+       "revision-info": "{{GENDER:$6|$2}}$7 के द्वारा $1 के संशोधन",
        "previousrevision": "← पुरान संशोधन",
        "nextrevision": "नया संशोधन →",
        "currentrevisionlink": "हाल के संशोधन",
        "history-feed-empty": "अनुरोध करल गईल पन्ना उपलब्ध नईखे। हो सकत बा उ के विकि पर से मिटा दिहल गईल होखे, आ चाहे उ के नाम बदल देवल गईल होखे।\nप्रासंगिक पन्ना के [[Special:Search|विकि पर खोजे के]] कोशिश करत रहीं।",
        "rev-deleted-comment": "(सम्पादन सारांश हटावल गईल)",
        "rev-deleted-user": "(प्रयोगकर्ता के नाम मिटा दिहल गईल बा)",
-       "rev-deleted-event": "(लà¥\89à¤\97 à¤\95ारà¥\8dरवाà¤\88 à¤®à¤¿टा दिहल गईल बा)",
+       "rev-deleted-event": "(लà¥\89à¤\97 à¤µà¤¿à¤µà¤°à¤£ à¤¹टा दिहल गईल बा)",
        "rev-deleted-user-contribs": "[प्रयोगकर्ता नाम आ चाहे आइ पी पता हटा दिहल गईल बा - योगदान से सम्पादन छुप गईल बा]",
        "rev-deleted-text-permission": "ई पन्ना के संशोधन '''मिटा'''' दिहल गईल बा।\nमेटावल जानकारी [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} डिलेशन लॉग] में मिली।",
        "rev-delundel": "दिखाईं/छुपाईं",
        "rev-showdeleted": "देखाईं",
        "revdelete-show-file-submit": "जी",
-       "revdelete-hide-text": "संशोधन पाठ्य छुपाँईं",
+       "revdelete-hide-text": "संशोधन पाठ्य",
        "revdelete-hide-image": "फाइल के सामग्री छुपाँईं",
-       "revdelete-hide-comment": "समà¥\8dपादित à¤¸à¤¾à¤°à¤¾à¤\82श à¤\9bà¥\81पाà¤\81à¤\88à¤\82",
-       "revdelete-hide-user": "समà¥\8dपादà¤\95 à¤\95à¥\87 à¤¸à¤¦à¤¸à¥\8dयनाम/à¤\86à¤\87॰पà¥\80 à¤\9bà¥\81पाà¤\81à¤\88à¤\82",
+       "revdelete-hide-comment": "साराà¤\82श à¤¸à¤®à¥\8dपादन",
+       "revdelete-hide-user": "समà¥\8dपादà¤\95 à¤\95à¥\87 à¤¸à¤¦à¤¸à¥\8dयनाम/à¤\86à¤\87॰पà¥\80 à¤ªà¤¤à¤¾",
        "revdelete-radio-same": "(मत बदलीं)",
-       "revdelete-radio-set": "à¤\9cà¥\80",
-       "revdelete-radio-unset": "ना",
+       "revdelete-radio-set": "à¤\9bà¥\81पल",
+       "revdelete-radio-unset": "दà¥\83शà¥\8dयमान",
        "revdelete-log": "कारण:",
        "revdel-restore": "दूश्यता बदलीं",
        "pagehist": "पन्ना के इतिहास",
        "shown-title": "दिखाईं $1 {{PLURAL:$1|परिणाम}}",
        "viewprevnext": "देखीं ($1 {{int:pipe-separator}} $2) ($3)",
        "searchmenu-exists": "'''इ विकि पर ''[[:$1]]'' नाम से एगो पन्ना उपलब्ध बा'''",
-       "searchmenu-new": "'''इ विकि पर ''[[:$1]]'' नाम से पन्ना बनाईं'''",
+       "searchmenu-new": "<strong> ई विकि पर \"[[:$1]]\" नाम के पन्ना बनाईं !</strong> {{PLURAL:$2|0=|आपके खोज से मिलल पन्ना भी देखीं|खोज परिणाम भी देखीं।}}",
        "searchprofile-articles": "सामग्री पन्ना",
        "searchprofile-images": "मल्टिमीडिया",
        "searchprofile-everything": "सब कुछ",
        "search-section": "(खंड $1)",
        "search-suggest": "का राउर मतलब बा: $1",
        "search-interwiki-caption": "भ्रातृ परियोजना",
-       "search-interwiki-default": "$1 à¤\95े परिणाम:",
+       "search-interwiki-default": "$1 à¤¸े परिणाम:",
        "search-interwiki-more": "(अउर)",
        "search-relatedarticle": "संबंधित",
        "searchrelated": "संबंधित",
        "preferences": "वरीयता",
        "mypreferences": "राउर पसन्द",
        "prefs-edits": "सम्पादन संख्या",
+       "prefsnologintext2": "आपन वरीयता में बदलाव लावे खातिर प्रवेश करीं।",
        "prefs-skin": "त्वचा",
        "skin-preview": "पूर्वावलोकन",
+       "datedefault": "वरीयता नईखे",
        "prefs-user-pages": "सदस्य पन्ना",
        "prefs-personal": "सदस्य प्रोफाईल",
        "prefs-rc": "तुरंत भईल परिवर्तन",
        "contributions-title": " $1 खातिर प्रयोगकर्ता योगदान",
        "mycontris": "राउर योगदान के सूची",
        "nocontribs": "ई मानदंड से मिलत जुलत कौनो बदलाव ना मिलल।",
-       "uctop": "(शà¥\80रà¥\8dष)",
+       "uctop": "(हाल à¤\95à¥\87)",
        "month": "महिना से (आ उ से पहिले):",
        "year": "साल से (आ उ से पहिले):",
        "sp-contributions-newbies": "खाली नया खाता के योगदान देखीं।",
        "whatlinkshere-hidelinks": "$1 लिंक",
        "whatlinkshere-hideimages": "$1 फ़ाइल लिंक",
        "whatlinkshere-filters": "फिल्टर",
-       "blockip": "प्रतिबंधित प्रयोगकर्ता",
+       "blockip": "{{GENDER:$1|सदस्य}} अवरोधित करीं",
        "ipboptions": "२ घंटे:2 hours,१ दिन:1 day,३ दिन:3 days,१ हफ्ता:1 week,२ हफ्ते:2 weeks,१ महिना:1 month,३ महिने:3 months,६ महिने:6 months,१ साल:1 year,हमेशा खातिर:infinite",
        "blocklink": "निष्क्रिय",
        "unblocklink": "ताला खोलीं",
        "tooltip-ca-viewsource": "इ पन्ना के संरक्षित कर दिहल गईल बा। रऊआ एकर मूल देख सकत बानी।",
        "tooltip-ca-history": "ई पन्ना के पिछला संशोधन",
        "tooltip-ca-protect": "इ पन्ना के संरक्षित करीं।",
-       "tooltip-ca-unprotect": "à¤\87 à¤ªà¤¨à¥\8dना à¤\95à¥\87 à¤¸à¤\82रà¤\95à¥\8dषण à¤¹à¤\9fाà¤\88ं।",
+       "tooltip-ca-unprotect": "à¤\88 à¤ªà¤¨à¥\8dना à¤\95à¥\87 à¤¸à¥\81रà¤\95à¥\8dषा à¤¬à¤¦à¤²à¥\80ं।",
        "tooltip-ca-delete": "ई पन्ना मिटाईं",
        "tooltip-ca-move": "ई पन्ना के स्थांतरण करीं",
        "tooltip-ca-watch": "इ पन्ना के आपन ध्यानसूची में डालीं",
        "pageinfo-toolboxlink": "जानकारी पन्ना",
        "file-nohires": " उच्च गुणवत्ता उपलब्ध नईखे।",
        "svg-long-desc": "एस वी जी फाईल, नॉमिनल्ली $1 x $2 पिक्सल्स, फाईल के आकार $3",
-       "show-big-image": "पà¥\81रा à¤\97à¥\81णवतà¥\8dता",
+       "show-big-image": "मà¥\82ल à¤«à¤¾à¤\88ल",
        "file-info-gif-looped": "लूप्ड",
        "file-info-gif-frames": "$1 {{PLURAL:$1|फ्रेम}}",
        "file-info-png-looped": "लूप्ड",
index 7f60af1..cf0eab9 100644 (file)
        "feedback-bugornote": "Si podeu descriure un problema tècnic en detall, [$1 informeu-ne].\nAltrament, podeu fer servir un senzill formulari a continuació. El vostre comentari s'afegirà a la pàgina «[$3 $2]», juntament amb el vostre nom d'usuari i el navegador que esteu emprant.",
        "feedback-cancel": "Canceŀla",
        "feedback-close": "Fet",
+       "feedback-error-title": "Error",
        "feedback-error1": "Error: Resultat de l'API no reconegut",
        "feedback-error2": "Error: Edició fallida",
        "feedback-error3": "Error: No hi ha resposta de l'API",
        "feedback-subject": "Assumpte:",
        "feedback-submit": "Envia",
        "feedback-thanks": "Gràcies! S'ha publicat la vostra opinió a la pàgina «[$2 $1]».",
+       "feedback-thanks-title": "Gràcies!",
        "searchsuggest-search": "Cerca",
        "searchsuggest-containing": "que conté ...",
        "api-error-badaccess-groups": "No teniu permís per a carregar fitxers en aquest wiki.",
index a45bb53..03183de 100644 (file)
@@ -18,7 +18,7 @@
        "tog-extendwatchlist": "Шорбина тӀехьажарна могӀам, ша беригге а хийцамаш чубогӀуш, тӀехьаббина боцурш а",
        "tog-usenewrc": "Лелабе дика могӀам керла чу хийцамашна (оьшу JavaScript)",
        "tog-numberheadings": "Ша шех хlитто терахь корташна",
-       "tog-showtoolbar": "Ð\93айÑ\82а Ð»Ð°ÐºÑ\85аÑ\80а Ð³Ó\80иÑ\80Ñ\81ан Ð´Ð°ÐºÑ\8aа Ð½Ð¸Ñ\81йеÑ\88 Ð°Ñ\82Ñ\82он Ð¾Ñ\86 Ñ\82адаÑ\80 Ñ\87оÑ\85Ñ\8c (JavaScript)",
+       "tog-showtoolbar": "Ð\93айÑ\82а Ð»Ð°ÐºÑ\85аÑ\80а Ð³Ó\80иÑ\80Ñ\81ийн Ð¿Ð°Ð½ÐµÐ»Ñ\8c Ñ\82адаÑ\80Ñ\88 Ð´ÐµÑ\87Ñ\83 Ñ\85енаÑ\85Ñ\8c",
        "tog-editondblclick": "Нисъе агӀонаш шозза тӀетаӀийча (JavaScript)",
        "tog-editsectiononrightclick": "Нисде дакъа шозза бакъехьар дахка тӀетаӀийча оцу кортан (JavaScript)",
        "tog-watchcreations": "ТӀетоха ас кхоьллина агӀонаш тергаме могӀам чу",
        "searchprofile-articles-tooltip": "$1 чохь лахар",
        "searchprofile-images-tooltip": "Файлаш лахар",
        "searchprofile-everything-tooltip": "Массо агӀонашкахь лахар (дийцаре агӀонашца)",
-       "searchprofile-advanced-tooltip": "Ð\94еÑ\85аÑ\80Ñ\86а Ð¹Ð¾Ð»Ñ\83 Ñ\86Ó\80еÑ\80ийн Ð¼ÐµÑ\82Ñ\82игашкахь лахар",
+       "searchprofile-advanced-tooltip": "Ð\94еÑ\85аÑ\80Ñ\86а Ð¹Ð¾Ð»Ñ\83 Ñ\86Ó\80еÑ\80ийн Ð°Ð½ашкахь лахар",
        "search-result-size": "$1 ({{PLURAL:$2|$2 дош|$2 дешнаш}})",
        "search-result-category-size": "$1 {{PLURAL:$1|юкъаяр}} ($2 {{PLURAL:$2|1=бухара категори|бухара категореш}}, $3 {{PLURAL:$3|1=файл|файлаш}}).",
        "search-redirect": "(дlасахьажийна $1)",
        "search-showingresults": "{{PLURAL:$4|Карийна <strong>$1</strong> — цхьаъ агӀо|И дош карийна <strong>$3</strong> агӀонашкахь, царех гойту $2 агӀо}}",
        "search-nonefound": "Дехаре терра цхьа хӀума ца карийна.",
        "powersearch-legend": "Шуьйра лахар",
-       "powersearch-ns": "ЦÓ\80еÑ\80ийн Ð¼ÐµÑ\82Ñ\82игашкахь лахар:",
+       "powersearch-ns": "ЦÓ\80еÑ\80ийн Ð°Ð½ашкахь лахар:",
        "powersearch-togglelabel": "Билгалдан:",
        "powersearch-toggleall": "Массо",
        "powersearch-togglenone": "ХӀумма цаоьшу",
index aad05e8..e2b0a8f 100644 (file)
        "logentry-block-reblock": "$1 {{GENDER:$2|změnil|změnila|mění}} nastavení bloku {{GENDER:$4|uživatele|uživatelky}} „$3“ s časem vypršení $5 $6",
        "logentry-suppress-block": "$1 {{GENDER:$2|zablokoval|zablokovala|blokuje}} {{GENDER:$4|uživatele|uživatelku}} „$3“ s časem vypršení $5 $6",
        "logentry-suppress-reblock": "$1 {{GENDER:$2|změnil|změnila|mění}} nastavení bloku {{GENDER:$4|uživatele|uživatelky}} „$3“ s časem vypršení $5 $6",
+       "logentry-import-upload": "$1 {{GENDER:$2|naimportoval|naimportovala}} $3 načtením souboru",
+       "logentry-import-interwiki": "$1 {{GENDER:$2|naimportoval|naimportovala}} $3 z jiné wiki",
        "logentry-merge-merge": "$1 {{GENDER:$2|sloučil|sloučila}} stránku $3 do $4 (revize po $5)",
        "logentry-move-move": "$1 {{GENDER:$2|přesunul|přesunula}} stránku $3 na $4",
        "logentry-move-move-noredirect": "$1 {{GENDER:$2|přesunul|přesunula}} stránku $3 na $4 bez založení přesměrování",
        "feedback-bugornote": "Pokud dokážete podrobně popsat technický problém, můžete [$1 nahlásit chybu].\nJinak můžete využít jednoduchý formulář níže. Váš komentář bude přidán na stránku „[$3 $2]“ spolu s vaším uživatelským jménem a informací o tom, jaký prohlížeč používáte.",
        "feedback-cancel": "Storno",
        "feedback-close": "Hotovo",
+       "feedback-external-bug-report-button": "Založit technický úkol",
        "feedback-dialog-title": "Odeslat názor",
-       "feedback-dialog-intro": "Pomocí níže zobrazeného formuláře můžete odeslat svůj názor. Váš komentář se spolu s vaším uživatelským jménem přidá na stránku „$1“.",
+       "feedback-dialog-intro": "Pomocí níže zobrazeného jednoduchého formuláře můžete odeslat svůj názor. Váš komentář se spolu s vaším uživatelským jménem přidá na stránku „$1“.",
        "feedback-error-title": "Chyba",
        "feedback-error1": "Chyba: Nerozpoznaný výsledek z API",
        "feedback-error2": "Chyba: Editace se nezdařila",
        "feedback-message": "Zpráva:",
        "feedback-subject": "Předmět:",
        "feedback-submit": "Odeslat",
+       "feedback-terms": "Jsem srozuměn s tím, že informace o mém uživatelském agentu zahrnují informace o přesné verzi mého prohlížeče a operačního systému a budou zveřejněny u mého komentáře.",
        "feedback-thanks": "Děkujeme! Váš komentář byl přidán na stránku „[$2 $1]“.",
        "feedback-thanks-title": "Děkujeme!",
+       "feedback-useragent": "Uživatelský agent:",
        "searchsuggest-search": "Hledat",
        "searchsuggest-containing": "obsahující…",
        "api-error-badaccess-groups": "Nemáte povoleno nahrávat soubory na tuto wiki.",
index 4a8444a..3b9ef84 100644 (file)
@@ -47,7 +47,8 @@
                        "Tjernobyl",
                        "Urhixidur",
                        "아라",
-                       "Thomsen"
+                       "Thomsen",
+                       "Knud Winckelmann"
                ]
        },
        "tog-underline": "Understreg henvisninger:",
        "revdelete-hide-image": "Skjul filindhold",
        "revdelete-hide-name": "Skjul handling og mål",
        "revdelete-hide-comment": "Redigeringssammendrag",
-       "revdelete-hide-user": "Brugerens brugernavn/IP-adrsse",
+       "revdelete-hide-user": "Brugerens brugernavn/IP-adresse",
        "revdelete-hide-restricted": "Skjul også informationen for administratorer",
        "revdelete-radio-same": "(ingen ændring)",
        "revdelete-radio-set": "Skjult",
index 34739ea..06815fa 100644 (file)
        "sp-contributions-uploads": "Hochgeladene Dateien",
        "sp-contributions-logs": "Logbücher",
        "sp-contributions-talk": "Diskussion",
-       "sp-contributions-userrights": "Benutzerrechteverwaltung",
+       "sp-contributions-userrights": "Benutzerrechte­verwaltung",
        "sp-contributions-blocked-notice": "{{GENDER:$1|Dieser Benutzer|Diese Benutzerin|Dieser Benutzer}} ist derzeit gesperrt. Es folgt der aktuelle Eintrag aus dem Benutzersperr-Logbuch:",
        "sp-contributions-blocked-notice-anon": "Diese IP-Adresse ist zurzeit gesperrt.\nZur Information folgt der aktuelle Auszug aus dem Sperr-Logbuch:",
        "sp-contributions-search": "Suche nach Benutzerbeiträgen",
        "logentry-block-reblock": "$1 {{GENDER:$2|änderte}} die Sperreinstellungen für {{GENDER:$4|$3}} mit einer Sperrdauer von $5 $6",
        "logentry-suppress-block": "$1 {{GENDER:$2|sperrte}} {{GENDER:$4|$3}} mit einer Sperrdauer von $5 $6",
        "logentry-suppress-reblock": "$1 {{GENDER:$2|änderte}} die Sperreinstellungen für {{GENDER:$4|$3}} mit einer Sperrdauer von $5 $6",
+       "logentry-import-upload": "$1 {{GENDER:$2|importierte}} $3 durch das Hochladen einer Datei",
+       "logentry-import-interwiki": "$1 {{GENDER:$2|importierte}} $3 aus einem anderen Wiki",
        "logentry-merge-merge": "$1 {{GENDER:$2|vereinigte}} $3 in die Seite „$4“ (Versionen bis zum $5)",
        "logentry-move-move": "$1 {{GENDER:$2|verschob}} die Seite $3 nach $4",
        "logentry-move-move-noredirect": "$1 {{GENDER:$2|verschob}} die Seite $3 nach $4, ohne dabei eine Weiterleitung anzulegen",
index 538ee08..a9a62b2 100644 (file)
        "fileexists-extension": "Un file cun l'istès nòm al ghé bèle: [[$2|thumb]]\n*Nòm dal file carghê: <strong>[[:$1]]</strong>\n*Nòm dal file che gh'é: <strong>[[:$2]]</strong>  \nVōt fōrsi siēlier un nòm pió carateréstich?",
        "fileexists-thumbnail-yes": "Al file carghê al sèmbra èsere 'na miniadûra ''(thumbnail)''. [[$1|thumb]] Verifichêr, per cunfròunt, al file <strong>[[:$1]]</strong>. Se 's trâta ed l'istèsa figûra, int al j amzûri uriginêli, an n'é mìa necesâri carghêr êtri miniadûri.",
        "file-thumbnail-no": "Al nòm dal file al cumincia cun <strong>$1</strong>; la sèmbra dòunca èser 'na miniadûra ''(thumbnail)''. Se 's gh'à la figûra int la qualitê uriginêla, as prèiga 'd carghêrla. In chêş contrâri, as prèiga 'd cambiêr al nòm dal file.",
-       "fileexists-forbidden": "Un file cun un nòm cumpâgn al gh'é bèle e an 's pōl mìa scrévregh inséma. Tunêr indrē e cambiêr al nòm cun al nòm ch'es vōl carghêr alò file. [[File:$1|thumb|center|$1]]",
+       "fileexists-forbidden": "Un file cun un nòm cumpâgn al gh'é bèle e an 's pōl mìa scrévregh inséma. Tunêr indrē e cambiêr al nòm cun al nòm ch'es vōl carghêr al file. [[File:$1|thumb|center|$1]]",
+       "fileexists-shared-forbidden": "Un file cun un nòm cumpâgn al gh'é bèle int l'archévi dal risórsi multimediêli spartîdi. S' es vōl incòra carghêr al file, tunêr indrē e cambiêr al nòm cun al nòm ch'es vōl carghêr al file. [[File:$1|thumb|center|$1]]",
+       "file-exists-duplicate": "Cōl file ché l'é 'na côpia {{PLURAL:$1|dal segvèint|di segvèint}} file:",
+       "file-deleted-duplicate": "Un file cumpâgn a còst ([[:$1]]) l'é stê scanşlê tèimp fa. Verifichêr la stôria dal scanşladûri préma 'd carghêrel incòra ed nōv.",
+       "file-deleted-duplicate-notitle": "Un file cumpâgn a còst l'é stê scanşlê tèimp fa, e al tétol l'é stê tôt via. Dmânda a quelchidûn  ch' al gh'à la pusibilitê ed vèdeer i file scanşlê 'd verifichêr la situasiòun préma 'd andêr avânti cun al carghêrel incòra 'd nōv.",
+       "uploadwarning": "Avîş che s'é drē carghêr",
+       "uploadwarning-text": "Per piaşèir câmbia ché sòta la spiegasiòun dal file e prōv' incòra.",
+       "savefile": "Sêlva 'l file",
+       "uploaddisabled": "La cârga dal file l'é bluchêda.",
+       "copyuploaddisabled": "La cârga per mèz  'd URL l'é blucêda.",
+       "uploaddisabledtext": "La cârga di file an n'é mìa atîva.",
        "license": "Licèinsa:",
        "license-header": "Licèinsa",
        "nolicense": "Nisóna licèinsa sgnêda",
index b40dadb..56bfbbc 100644 (file)
        "editingsection": "Editing $1 (section)",
        "editingcomment": "Editing $1 (new section)",
        "editconflict": "Edit conflict: $1",
+       "editnotice-notext": "-",
        "explainconflict": "Someone else has changed this page since you started editing it.\nThe upper text area contains the page text as it currently exists.\nYour changes are shown in the lower text area.\nYou will have to merge your changes into the existing text.\n<strong>Only</strong> the text in the upper text area will be saved when you press \"{{int:savearticle}}\".",
        "yourtext": "Your text",
        "storedversion": "Stored revision",
index 38a9aa8..6d2b569 100644 (file)
        "logentry-block-reblock": "$1 {{GENDER:$2|cambió}} la configuración del bloqueo de {{GENDER:$4|$3}} durante un plazo de $5 $6",
        "logentry-suppress-block": "$1 {{GENDER:$2|bloqueó}} {{GENDER:$4|$3}} durante un plazo de $5 $6",
        "logentry-suppress-reblock": "$1 {{GENDER:$2|cambió}} la configuración del bloqueo de {{GENDER:$4|$3}} durante un plazo de $5 $6",
+       "logentry-import-upload": "$1 {{GENDER:$2|importó}} $3 subiendo un archivo",
+       "logentry-import-interwiki": "$1 {{GENDER:$2|importó}} $3 desde otro wiki",
        "logentry-merge-merge": "$1 {{GENDER:$2|combinó}} $3 en $4 (revisiones hasta el $5)",
        "logentry-move-move": "$1 {{GENDER:$2|trasladó}} la página $3 a $4",
        "logentry-move-move-noredirect": "$1 movió la página $3 a $4 sin dejar una redirección",
index 270c12c..46ca745 100644 (file)
        "logentry-block-reblock": "$1 {{GENDER:$2|muutis}} kasutaja {{GENDER:$4|$3}} blokeeringut; aegumistähtaeg $5 $6",
        "logentry-suppress-block": "$1 {{GENDER:$2|blokeeris}} kasutaja {{GENDER:$4|$3}}; aegumistähtaeg $5 $6",
        "logentry-suppress-reblock": "$1 {{GENDER:$2|muutis}} kasutaja {{GENDER:$4|$3}} blokeeringut; aegumistähtaeg $5 $6",
+       "logentry-import-upload": "$1 {{GENDER:$2|importis}} lehekülje $3 faili üleslaadimise teel",
+       "logentry-import-interwiki": "$1 {{GENDER:$2|importis}} lehekülje $3 teisest vikist",
        "logentry-merge-merge": "$1 {{GENDER:$2|liitis}} lehekülje $3 leheküljega $4 (kuni redaktsioonini $5)",
        "logentry-move-move": "$1 {{GENDER:$2|teisaldas}} lehekülje $3 pealkirja $4 alla",
        "logentry-move-move-noredirect": "$1 {{GENDER:$2|teisaldas}} lehekülje $3 pealkirja $4 alla ümbersuunamist maha jätmata",
index 2661cd8..5e7a0b6 100644 (file)
        "tags-delete-title": "Hävitä merkkaus pysyvästi",
        "tags-delete-explanation-initial": "Olet parhaillaan poistamassa (eli tuhoamassa pysyvästi) merkkauksen \"$1\" tietokannasta.",
        "tags-delete-explanation-in-use": "Se poistuu {{PLURAL:$2|$2 sivun versiosta tai lokimerkinnästä|kaikista $2 sivuversiosta tai lokimerkinnöistä}}, joissa se tällä hetkellä on käytössä.",
-       "tags-delete-explanation-warning": "Tämä toimenpide on <strong>peruuttamaton</strong> ja <strong>poistettua ei merkkausta voida palauttaa takaisin</strong>. Siihen ei pysty edes tietokannan ylläpitohenkilöstö. Sinun pitää olla täysin varma, että haluat poistettavaksi juuri tämän merkkauksen.",
+       "tags-delete-explanation-warning": "Tämä toimenpide on <strong>peruuttamaton</strong> ja <strong>poistettua merkkausta ei voi palauttaa takaisin</strong>. Siihen ei pysty edes tietokannan ylläpitohenkilöstö. Sinun pitää olla täysin varma, että haluat poistettavaksi juuri tämän merkkauksen.",
        "tags-delete-explanation-active": "<stron>Merkkaus \"$1\" on edelleen käytössä ja sitä käytetään myös jatkossa.</strong>. Jos haluat merkkauksen pois käytöstä, mene sinne missä merkkaus on asetettu ja ota se pois siellä.",
        "tags-delete-reason": "Syy:",
        "tags-delete-submit": "Tuhoa tämä merkkaus peruuttamattomasti ja pysyvästi",
        "logentry-block-reblock": "$1 {{GENDER:$2|muutti}} eston asetuksia kohteessa {{GENDER:$4|$3}}. Eston kesto on $5 $6",
        "logentry-suppress-block": "$1 {{GENDER:$2|esti}} käyttäjän {{GENDER:$4|$3}}. Eston kesto on $5 $6",
        "logentry-suppress-reblock": "$1 {{GENDER:$2|muutti}} eston asetuksia kohteessa {{GENDER:$4|$3}}. Eston kesto on $5 $6",
+       "logentry-import-upload": "$1 {{GENDER:$2|toi}} kohteen $3 tiedostotallennuksella",
+       "logentry-import-interwiki": "$1 {{GENDER:$2|toi}} kohteen $3 muusta wikistä",
        "logentry-merge-merge": "$1 {{GENDER:$2|yhdisti}} sivun $3 sivuun $4 (versiot $5 saakka)",
        "logentry-move-move": "$1 {{GENDER:$2|siirsi}} sivun $3 uudelle nimelle $4",
        "logentry-move-move-noredirect": "$1 {{GENDER:$2|siirsi}} sivun $3 uudelle nimelle $4 luomatta ohjausta",
index 2d2f98c..c8ab36f 100644 (file)
        "logentry-block-reblock": "$1 {{GENDER:$2|a modifié}} les paramètres de blocage pour {{GENDER:$4|$3}} $5 $6",
        "logentry-suppress-block": "$1 {{GENDER:$2|a bloqué}} {{GENDER:$4|$3}} $5 $6",
        "logentry-suppress-reblock": "$1 {{GENDER:$2|a modifié}} les paramètres de blocage pour {{GENDER:$4|$3}} $5 $6",
+       "logentry-import-upload": "$1 {{GENDER:$2|a importé}} $3 par téléchargement de fichier",
+       "logentry-import-interwiki": "$1 {{GENDER:$2|a importé}} $3 depuis un autre wiki",
        "logentry-merge-merge": "$1 {{GENDER:$2|a fusionné}} $3 en $4 (révisions jusqu’à $5)",
        "logentry-move-move": "$1 {{GENDER:$2|a déplacé}} la page $3 vers $4",
        "logentry-move-move-noredirect": "$1 {{GENDER:$2|a déplacé}} la page $3 vers $4 sans laisser de redirection",
index 27f0b91..c530e1e 100644 (file)
        "enhancedrc-history": "Ferluup",
        "recentchanges": "Leetst feranrangen",
        "recentchanges-legend": "Iinstelangen för't uunwisin",
-       "recentchanges-summary": "Üüb detdiar sidj könst dü a leetst feranrangen faan't Nordfriisk Wikipedia ferfulge.",
+       "recentchanges-summary": "Üüb detdiar sidj könst dü a leetst feranrangen faan detheer Wiki-projekt ferfulge.",
        "recentchanges-noresult": "Uun di uunjiwen tidjrüm san sok feranrangen ei föörnimen wurden.",
-       "recentchanges-feed-description": "Mä didiar feed könst dü a leetst feranrangen faan't Nordfriisk Wikipedia ferfulge.",
+       "recentchanges-feed-description": "Mä didiar feed könst dü a leetst feranrangen faan detheer Wiki-projekt ferfulge.",
        "recentchanges-label-newpage": "Nei sidj uunlaanj",
        "recentchanges-label-minor": "Letj feranrang",
        "recentchanges-label-bot": "Feranrang faan en bot",
index 77157e9..f3bdf85 100644 (file)
        "logentry-block-reblock": "$1 {{GENDER:$2|cambiou}} as configuracións do bloqueo de {{GENDER:$4|$3}} cunha caducidade de $5 $6",
        "logentry-suppress-block": "$1 {{GENDER:$2|bloqueou}} a {{GENDER:$4|$3}} cun tempo de duración de $5 $6",
        "logentry-suppress-reblock": "$1 {{GENDER:$2|cambiou}} as configuracións do bloqueo de {{GENDER:$4|$3}} cunha caducidade de $5 $6",
+       "logentry-import-upload": "$1 {{GENDER:$2|importou}} $3 por medio da carga de ficheiros",
+       "logentry-import-interwiki": "$1 {{GENDER:$2|importou}} $3 dende outra wiki",
        "logentry-merge-merge": "$1 {{GENDER:$2|fusionou}} \"$3\" con \"$4\" (revisións ata o $5)",
        "logentry-move-move": "$1 {{GENDER:$2|moveu}} a páxina \"$3\" a \"$4\"",
        "logentry-move-move-noredirect": "$1 {{GENDER:$2|moveu}} a páxina \"$3\" a \"$4\" sen deixar unha redirección",
index 0d3581d..5ae57bb 100644 (file)
        "logentry-block-reblock": "$1 {{GENDER:$2|שינה|שינתה}} את הגדרות החסימה של {{GENDER:$4|$3}} עם זמן פקיעה של $5 $6",
        "logentry-suppress-block": "$1 {{GENDER:$2|חסם|חסמה}} את {{GENDER:$4|$3}} עם זמן פקיעה של $5 $6",
        "logentry-suppress-reblock": "$1 {{GENDER:$2|שינה|שינתה}} את הגדרות החסימה של {{GENDER:$4|$3}} עם זמן פקיעה של $5 $6",
+       "logentry-import-upload": "$1 {{GENDER:$2|ייבא|ייבאה}} את $3 באמצעות העלאת קובץ",
+       "logentry-import-interwiki": "$1 {{GENDER:$2|ייבא|ייבאה}} את $3 מאתר ויקי אחר",
        "logentry-merge-merge": "$1 {{GENDER:$2|מיזג|מיזגה}} את $3 לתוך $4 (גרסאות עד $5)",
        "logentry-move-move": "$1 {{GENDER:$2|העביר|העבירה}} את הדף $3 ל{{GRAMMAR:תחילית|$4}}",
        "logentry-move-move-noredirect": "$1 {{GENDER:$2|העביר|העבירה}} את הדף $3 ל{{GRAMMAR:תחילית|$4}} בלי להשאיר הפניה",
index aab22f2..d5e8d28 100644 (file)
@@ -57,7 +57,8 @@
                        "아라",
                        "संजीव कुमार",
                        "बिप्लब आनन्द",
-                       "Phoenix303"
+                       "Phoenix303",
+                       "Steinsplitter"
                ]
        },
        "tog-underline": "कड़ियाँ अधोरेखन:",
        "disclaimerpage": "Project:साधारण अस्वीकरण",
        "edithelp": "सम्पादन सहायता",
        "helppage-top-gethelp": "सहायता",
-       "mainpage": "मुखपृष्ठ",
-       "mainpage-description": "मुखपृष्ठ",
+       "mainpage": "'मुख्य पृष्ठ",
+       "mainpage-description": "मà¥\81à¤\96à¥\8dय à¤ªà¥\83षà¥\8dठ",
        "policy-url": "Project:नीति",
        "portal": "समाज मुखपृष्ठ",
        "portal-url": "Project:समाज मुखपृष्ठ",
        "right-override-export-depth": "पृष्ठ निर्यात करें, पाँच स्तर की गहराई तक जुड़े हुए पृष्ठों समेत",
        "right-sendemail": "अन्य सदस्यों को ई-मेल भेजें",
        "right-passwordreset": "कूटशब्द रीसेट ई-मेल देखें",
+       "right-managechangetags": "Create and delete [[Special:Tags|tags]] from the database",
        "newuserlogpage": "सदस्य खाता निर्माण लॉग",
        "newuserlogpagetext": "यह सदस्य खातों के निर्माण का लॉग है।",
        "rightslog": "सदस्य अधिकार लॉग",
        "action-viewmyprivateinfo": "अपनी व्यक्तिगत जानकारी देखने",
        "action-editmyprivateinfo": "अपनी व्यक्तिगत जानकारी बदलने",
        "action-editcontentmodel": "एक पेज की सामग्री मॉडल को संपादित।",
+       "action-managechangetags": "डेटाबेस से टैग बनाए और हटाए",
        "nchanges": "$1 {{PLURAL:$1|बदलाव}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|अंतिम बार देखने के बाद से}}",
        "enhancedrc-history": "इतिहास",
        "randomincategory-invalidcategory": "\"$1\" एक मान्य श्रेणी नाम नहीं है।",
        "randomincategory-nopages": "[[:Category:$1|$1]] श्रेणी में कोई पृष्ठ नहीं हैं।",
        "randomincategory-category": "श्रेणी:",
+       "randomincategory-legend": "श्रेणीमें से कोईभी पन्ना",
        "randomredirect": "किसी एक पुनर्निर्देशन पर जाएँ",
        "randomredirect-nopages": "नामस्थान \"$1\" में कोई पुनर्निर्देशन नहीं हैं।",
        "statistics": "आँकड़े",
        "tags-create-submit": "बनाएँ",
        "tags-create-no-name": "आपको एक टैग नाम निर्दिष्ट करना होगा।",
        "tags-delete-title": "टैग हटाएं",
+       "tags-activate-submit": "सक्रिय करें",
+       "tags-deactivate-reason": "कारण:",
+       "tags-deactivate-submit": "सक्रिय करें",
        "comparepages": "पृष्ठों की तुलना करें",
        "compare-page1": "पृष्ठ १",
        "compare-page2": "पृष्ठ २",
        "htmlform-no": "नहीं",
        "htmlform-yes": "हाँ",
        "htmlform-chosen-placeholder": "एक विकल्प चुनें",
+       "htmlform-cloner-create": "अधिक जोड़ें",
        "htmlform-cloner-delete": "हटाएँ",
        "sqlite-has-fts": "$1 पूर्ण पाठ खोज समर्थन के साथ",
        "sqlite-no-fts": "$1पूर्ण-पाठ खोज समर्थन के बिना",
index 7bff356..7fb5118 100644 (file)
        "eauthentsent": "Egy ellenőrző e-mailt küldtünk a megadott címre. Mielőtt más leveleket kaphatnál, igazolnod kell az e-mailben írt utasításoknak megfelelően, hogy valóban a tiéd a megadott cím.",
        "throttled-mailpassword": "Már elküldtünk egy jelszóemlékeztetőt az utóbbi {{PLURAL:$1|egy|$1}} órában.\nA visszaélések elkerülése végett {{PLURAL:$1|egy|$1}} óránként csak egy jelszó-emlékeztetőt küldünk.",
        "mailerror": "Hiba történt az e-mail küldése közben: $1",
-       "acct_creation_throttle_hit": "A wiki látogatói ezt az IP-címet használva {{PLURAL:$1|egy|$1}} fiókot hoztak létre az elmúlt egy nap alatt . Ez a megengedett maximum ezen időtartam alatt, így az erről a címről látogatók jelenleg nem hozhatnak létre újabb fiókokat.",
-       "emailauthenticated": "Az e-mail címedet $2 $3-kor erősítetted meg.",
+       "acct_creation_throttle_hit": "A wiki látogatói ezt az IP-címet használva $1 fiókot hoztak létre az elmúlt egy nap alatt. Ez a megengedett maximum ezen időtartam alatt, így az erről a címről látogatók jelenleg nem hozhatnak létre újabb fiókokat.",
+       "emailauthenticated": "Az e-mail címedet $2, $3-kor erősítetted meg.",
        "emailnotauthenticated": "Az e-mail címed még <strong>nincs megerősítve</strong>. E-mailek küldése és fogadása nem engedélyezett.",
        "noemailprefs": "Az alábbi funkciók használatához meg kell adnod az e-mail címedet.",
        "emailconfirmlink": "E-mail cím megerősítése",
        "speciallogtitlelabel": "Cél (cím vagy felhasználó):",
        "log": "Rendszernaplók",
        "all-logs-page": "Minden nyilvános napló",
-       "alllogstext": "A(z) {{SITENAME}} naplóinak összesített listája.\nA napló típusának, a szerkesztő nevének (kis- és nagybetűérzékeny), vagy az érintett lap kiválasztásával (ez is kis- és nagybetűérzékeny) szűkítheted a találatok listáját.",
+       "alllogstext": "A(z) {{SITENAME}} naplóinak összesített listája.\nA naplótípus, a felhasználónév (kis- és nagybetűérzékeny) vagy az érintett lap kiválasztásával (ez is kis- és nagybetűérzékeny) szűkítheted a találatok listáját.",
        "logempty": "Nincs illeszkedő naplóbejegyzés.",
        "log-title-wildcard": "Így kezdődő címek keresése",
        "showhideselectedlogentries": "Kijelölt napló bejegyzések megjelenítése/elrejtése",
        "trackingcategories-nodesc": "Nem található leírás.",
        "trackingcategories-disabled": "A kategória le van tiltva",
        "mailnologin": "Nincs feladó",
-       "mailnologintext": "Ahhoz hogy másoknak e-mailt küldhess, [[Special:UserLogin|be kell jelentkezned]] és meg kell adnod egy érvényes e-mail címet a [[Special:Preferences|beállításaidban]].",
+       "mailnologintext": "Ahhoz, hogy másoknak e-mailt küldhess, [[Special:UserLogin|be kell jelentkezned]] és meg kell adnod egy érvényes e-mail címet a [[Special:Preferences|beállításaidban]].",
        "emailuser": "E-mail küldése ezen szerkesztőnek",
        "emailuser-title-target": "E-mail küldése ennek a felhasználónak: $1",
        "emailuser-title-notarget": "E-mail küldése a felhasználónak",
        "blocklog-showsuppresslog": "Ez a felhasználó korábban blokkot kapott, és a naplóbejegyzés el lett rejtve. Az elrejtési napló alább látható tájékoztatásként:",
        "blocklogentry": "„[[$1]]” blokkolva $2 időtartamra $3",
        "reblock-logentry": "megváltoztatta [[$1]] blokkjának beállításait, a blokk lejárta: $2 $3",
-       "blocklogtext": "Ez a felhasználókra helyezett blokkoknak és azok feloldásának listája. Az automatikus blokkolt IP címek nem szerepelnek a listában. Lásd még [[Special:BlockList|a jelenleg életben lévő blokkok listáját]].",
+       "blocklogtext": "Ez a felhasználókra helyezett blokkoknak és azok feloldásának listája. Az automatikusan blokkolt IP-címek nem szerepelnek a listában. Lásd még [[Special:BlockList|a jelenleg életben lévő blokkok listáját]].",
        "unblocklogentry": "„$1” blokkolása feloldva",
        "block-log-flags-anononly": "csak anonok",
        "block-log-flags-nocreate": "nem hozhat létre új fiókot",
        "import-options-wrong": "Rossz {{PLURAL:$2|opció|opciók}}: <nowiki>$1</nowiki>",
        "import-rootpage-invalid": "A megadott gyökér oldal címe érvénytelen.",
        "import-rootpage-nosubpage": "A(z) \"$1\" névtér nem engedi meg aloldalak használatát.",
-       "importlogpage": "Importnapló",
+       "importlogpage": "Importálási napló",
        "importlogpagetext": "Lapok szerkesztési előzményekkel történő adminisztratív imporálása más wikikből.",
        "import-logentry-upload-detail": "$1 változat importálva",
        "import-logentry-interwiki-detail": "$1 változat innen: $2",
        "confirmemail_sent": "Kaptál egy e-mailt, melyben megtalálod a megerősítéshez szükséges kódot.",
        "confirmemail_oncreate": "A megerősítő kódot elküldtük az e-mail címedre.\nEz a kód nem szükséges a belépéshez, de meg kell adnod,\nmielőtt a wiki e-mail alapú szolgáltatásait igénybe veheted.",
        "confirmemail_sendfailed": "Nem sikerült elküldeni a megerősítő e-mailt.\nEllenőrizd, hogy nem írtál-e érvénytelen karaktert a címbe.\n\nA levelező üzenete: $1",
-       "confirmemail_invalid": "Nem megfelelő kód. A kódnak lehet, hogy lejárt a felhasználhatósági ideje.",
-       "confirmemail_needlogin": "Meg kell $1 erősíteni az e-mail címedet.",
+       "confirmemail_invalid": "Nem megfelelő kód.\nLehet, hogy a kód felhasználhatósági ideje lejárt.",
+       "confirmemail_needlogin": "{{ucfirst:$1}} az e-mail címed megerősítéséhez.",
        "confirmemail_success": "Az e-mail címed megerősítve. Most már beléphetsz a wikibe.",
        "confirmemail_loggedin": "E-mail címed megerősítve.",
        "confirmemail_subject": "{{SITENAME}} e-mail cím megerősítés",
index ba263e4..9738363 100644 (file)
        "action-viewmyprivateinfo": "vider le proprie information private",
        "action-editmyprivateinfo": "modificar le proprie information private",
        "action-editcontentmodel": "modificar le modello de contento de un pagina",
+       "action-managechangetags": "crear e deler etiquettas in le base de datos",
        "nchanges": "$1 {{PLURAL:$1|modification|modificationes}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|desde le ultime visita}}",
        "enhancedrc-history": "historia",
        "thumbnail_image-missing": "le file pare mancar: $1",
        "thumbnail_image-failure-limit": "Il ha habite recentemente troppo de tentativas fallite ($1 o plus) de generar iste miniatura. Per favor reproba plus tarde.",
        "import": "Importar paginas",
-       "importinterwiki": "Importation transwiki",
+       "importinterwiki": "Importar ab un altere wiki",
        "import-interwiki-text": "Selige le wiki e le titulo del pagina a importar.\nLe datas del versiones e nomines del contributores essera preservate.\nTote le actiones de importation transwiki se registra in le [[Special:Log/import|registro de importationes]].",
        "import-interwiki-sourcewiki": "Wiki de origine:",
        "import-interwiki-sourcepage": "Pagina de origine:",
index bbb0581..940e34b 100644 (file)
@@ -34,6 +34,7 @@
        "tog-watchdefault": "Bæta síðum og skrám sem ég breyti á vaktlistann minn",
        "tog-watchmoves": "Bæta síðum og skrám sem ég færi á vaktlistann minn",
        "tog-watchdeletion": "Bæta síðum og skrám sem ég eyði á vaktlistann minn",
+       "tog-watchrollback": "Bæta síðum þar sem ég hef tekið aftur breytingu á vaktlistann minn",
        "tog-minordefault": "Merkja allar breytingar sem minniháttar sjálfgefið",
        "tog-previewontop": "Sýna forskoðun á undan breytingarkassanum",
        "tog-previewonfirst": "Sýna forskoðun með fyrstu breytingu",
@@ -44,7 +45,7 @@
        "tog-shownumberswatching": "Sýna fjölda vaktandi notenda",
        "tog-oldsig": "Núverandi undirskrift:",
        "tog-fancysig": "Meðhöndla undirskrift sem wikimál (án sjálfvirks tengils)",
-       "tog-uselivepreview": "Nota beina forskoðun (Á tilraunastigi)",
+       "tog-uselivepreview": "Nota beina forskoðun",
        "tog-forceeditsummary": "Birta áminningu þegar breytingarágripið er tómt",
        "tog-watchlisthideown": "Ekki sýna mínar breytingar á vaktlistanum",
        "tog-watchlisthidebots": "Ekki sýna breytingar vélmenna á vaktlistanum",
index 640673b..e49bade 100644 (file)
        "logentry-block-reblock": "$1 {{GENDER:$2|ha modificato}} le impostazioni del blocco per {{GENDER:$4|$3}} con una scadenza di $5 $6",
        "logentry-suppress-block": "$1 {{GENDER:$2|ha bloccato}} {{GENDER:$4|$3}} con una scadenza di $5 $6",
        "logentry-suppress-reblock": "$1 {{GENDER:$2|ha modificato}} le impostazioni del blocco per {{GENDER:$4|$3}} con una scadenza di $5 $6",
+       "logentry-import-upload": "$1 {{GENDER:$2|ha importato}} $3 tramite caricamento",
+       "logentry-import-interwiki": "$1 {{GENDER:$2|ha importato}} $3 da un'altra wiki",
        "logentry-merge-merge": "$1 {{GENDER:$2|ha unito}} $3 in $4 (versioni fino al $5)",
        "logentry-move-move": "$1 {{GENDER:$2|ha spostato}} la pagina $3 a $4",
        "logentry-move-move-noredirect": "$1 {{GENDER:$2|ha spostato}} la pagina $3 a $4 senza lasciare redirect",
index ca4f213..3c5f492 100644 (file)
@@ -38,6 +38,7 @@
        "tog-watchdefault": "დაამატე ჩემი კონტროლის სიას ჩემს მიერ რედაქტირებული გვერდები და ფაილები",
        "tog-watchmoves": "ჩასვი გვერდები და ფაილები, რომლებიც მე გადამაქვს, ჩემს კონტროლის სიაში",
        "tog-watchdeletion": "ჩასვი გვერდები და ფაილები, რომლებსაც მე ვშლი, ჩემს კონტროლის სიაში",
+       "tog-watchrollback": "დაამატე ჩემი კონტროლის სიას გვერდები, სადაც მე სწრაფი გაუქმება განვახორციელე",
        "tog-minordefault": "ყველა რედაქტირების მონიშვნა, როგორც უმნიშვნელო, უპირობოდ",
        "tog-previewontop": "წინასწარი გადახედვის ჩვენება რედაქტირების დაფამდე",
        "tog-previewonfirst": "წინასწარი ჩვენება პირველივე რედაქტირებაზე",
        "changeemail-password": "თქვენი პაროლი პროექტში {{SITENAME}}:",
        "changeemail-submit": "ელ-ფოსტის შეცვლა",
        "changeemail-throttled": "თქვენ უკვე ძალიან ბევრჯერ სცადეთ შესვლა.\nგთხოვთ, მოიცადოთ $1, სანამ კიდევ სცდიდეთ.",
+       "resettokens": "ტოკენების ჩამოყრა",
+       "resettokens-legend": "ტოკენების ჩამოყრა",
        "resettokens-tokens": "ჟეტონები:",
        "resettokens-token-label": "$1 (მიმდინარე მნიშვნელობა: $2)",
+       "resettokens-resetbutton": "არჩეული ტოკენების ჩამოყრა",
        "bold_sample": "მუქი ტექსტი",
        "bold_tip": "მუქი ტექსტი",
        "italic_sample": "კურსივი",
index 2d5a6c2..9d18201 100644 (file)
        "otherlanguages": "Басқа тілдерде",
        "redirectedfrom": "($1 бетінен бағытталған)",
        "redirectpagesub": "Бағыттау беті",
+       "redirectto": "Мына бетке бағытталған:",
        "lastmodifiedat": "Бұл беттің соңғы өзгертілген кезі: $2, $1.",
        "viewcount": "Бұл бет {{PLURAL:$1|бір рет|$1 уақыт}} қатыналған.",
        "protectedpage": "Қорғалған бет",
        "rcshowhideanons": "Кірмегендерді $1",
        "rcshowhideanons-show": "көрсету",
        "rcshowhideanons-hide": "жасыру",
-       "rcshowhidepatr": "Ð\97еÑ\80Ñ\82Ñ\82елген өңдемелерді $1",
+       "rcshowhidepatr": "ТекÑ\81еÑ\80Ñ\96лген өңдемелерді $1",
        "rcshowhidepatr-show": "көрсету",
        "rcshowhidepatr-hide": "жасыру",
        "rcshowhidemine": "Өңдемелерімді $1",
        "number_of_watching_users_pageview": "[бақылаған $1 қатысушы]",
        "rc_categories": "Санаттарға шектеу (\"|\" белгісімен бөліктеңіз)",
        "rc_categories_any": "Кез келген",
-       "rc-change-size-new": "Өңдеуден кейінгі көлемі: $1{{PLURAL:$1|байт|байт}}",
+       "rc-change-size-new": "Өңдеуден кейінгі көлемі: $1 {{PLURAL:$1|байт|байт}}",
        "newsectionsummary": "/* $1 */ жаңа бөлім",
        "rc-enhanced-expand": "Толық ақпаратты көрсету",
        "rc-enhanced-hide": "Толық ақпаратты жасыру",
        "importunknownsource": "Cырттан алынатын қайнар түрі белгісіз",
        "importcantopen": "Сырттан алынатын файл ашылмайды",
        "importbadinterwiki": "Жарамсыз уики-аралық сілтеме",
-       "importsuccess": "СÑ\8bÑ\80Ñ\82Ñ\82ан Ð°Ð»у аяқталды!",
+       "importsuccess": "Ð\98мпоÑ\80Ñ\82Ñ\82ау аяқталды!",
        "importnosources": "Уики-апару үшін сырттан алынатын еш қайнар көзі анықталмаған, және тарихын тікелей қотарып беруі өшірілген.",
        "importnofile": "Сырттан алынған файл жүктелген жоқ.",
        "importuploaderrorsize": "Импортталған файлдың жүктелуі сәтсіз болды. \nФайл рұқсат етілгеннен жүктеу өлшемінен үлкенірек.",
        "markedaspatrollederror": "Зерттелді деп белгіленбейді",
        "markedaspatrollederrortext": "Зерттелді деп белгілеу үшін түзетуді келтіріңіз.",
        "markedaspatrollederror-noautopatrol": "Өз жасаған өзгерістеріңізді зерттелді деп белгілей алмайсыз.",
-       "patrol-log-page": "Ð\97еÑ\80Ñ\82Ñ\82еу журналы",
-       "log-show-hide-patrol": "$1 зерттеу журналы",
+       "patrol-log-page": "ТекÑ\81еÑ\80у журналы",
+       "log-show-hide-patrol": "$1 тексеру журналы",
        "deletedrevision": "Ескі түзетуін жойды: $1",
        "filedeleteerror-short": "Файл жою қатесі: $1",
        "filedeleteerror-long": "Файлды жойғанда қателер кездесті:\n\n$1",
        "logentry-rights-rights-legacy": "$1 $3 үшін топ мүшелігін {{GENDER:$2|өзгерті}}",
        "logentry-upload-upload": "$1 $3 файлын {{GENDER:$2|жүктеді}}",
        "logentry-upload-overwrite": "$1 $3 дегеннің жаңа нұсқасын {{GENDER:$2|жүктеді}}",
+       "log-name-managetags": "Тег басқару журналы",
        "rightsnone": "(ешқандай)",
        "revdelete-summary": "өңдеменің қысқаша мазмұндамасы",
        "feedback-adding": "Бетке кері байланыс қосуда...",
index ee1bdc0..5204f14 100644 (file)
        "logentry-block-reblock": "{{GENDER:$2|Dä|Dat|Dä Metmaacher|De|Dat}} $1 hät {{GENDER:$4|däm|däm|däm Metmaacher|dä|däm}} $3 {{GENDER:$3|sing|sing|sing|ier|sing}} sing Schpärr för di Zigg vun $5 verändert. $6",
        "logentry-suppress-block": "{{GENDER:$2|Dä|Dat|Dä Metmaacher|De|Dat}} $1 hät {{GENDER:$4|dä|dat|dä Metmaacher|de|dat}} $3 för en Zigg vun $5 jespächt. $6",
        "logentry-suppress-reblock": "{{GENDER:$2|Dä|Dat|Dä Metmaacher|De|Dat}} $1 hät {{GENDER:$4|däm|däm|däm Metmaacher|dä|däm}} $3 {{GENDER:$3|sing|sing|sing|ier|sing}} sing Schpärr för di Zigg vun $5 verändert. $6",
+       "logentry-import-upload": "{{GENDER:$2|Dä|Dat|Dä Metmaacher|De|Dat}} „$1“ hät di Sigg „$3“ huhjelahde un esu empottehrt",
+       "logentry-import-interwiki": "{{GENDER:$2|Dä|Dat|Dä Metmaacher|De|Dat}} „$1“ hät di Sigg „$3“ fun enem andere Wikki empottehrt",
        "logentry-merge-merge": "{{GENDER:$2|Dä|Dat|Dä Metmaacher|De|Dat}} $1 hät di Sigg „$3“ met dä Sigg „$4“ zersamme jelaat, jenou jenumme de Väsjohne bes $5",
        "logentry-move-move": "{{GENDER:$2|Dä|Dat|Dä Metmaacher|De|Dat}} $1 hätt di Sigg „$3“ en „$4“ ömjenannt.",
        "logentry-move-move-noredirect": "{{GENDER:$2|Dä|Dat|Dä Metmaacher|De|Dat}} $1 hätt di Sigg „$3“ en „$4“ ömjenannt un derbei kein Ömleidong aanjelaat.",
        "log-name-pagelang": "Logbooch vum Tuusche vun Sige iehr Schprohche",
        "log-description-pagelang": "Dat heh es et Logbohch vun de Veränderonge aan de Schprohch vun de Sigge.",
        "logentry-pagelang-pagelang": "{{GENDER:$2|Dä|Dat|Dä Metmaacher|De|Dat}} $1 hät de Schprohch vun dä Sigg „$3“ vun $4 op $5 verändert.",
-       "default-skin-not-found": "De schtandattmähßejje Bedehnbovverfläsch <code>$1</code> för et Wikki es nit ze fenge. Se weed övver dä Enndraach <code lang=\"en\" xml:lang=\"en\">$wgDefaultSkin</code> en dä Dattei <code lang=\"en\" xml:lang=\"en\">LocalSettings.php</code> om ẞööver faßjelaat.\n\nHeh di Bedehnbovverfläsche sin doh:\n\n$2\n\nLohr och en et [https://www.mediawiki.org/wiki/Manual:Skin_configuration/de Handbohch övver et Enschtälle vun Bedehnbovverfläsche].\n\n*'''Falls dat heh e fresch enjereesch MeedijaWikki es:'''\n*: MeedijaWikki wood velleisch övver <i lang=\"en\" xml:lang=\"en\">Git</i> eschtallehrt, udder der Quälltäx wood tiräk obb_en ander Manier eschtallehrt. Met däm Problehm heh wohr ze rääschne. Donn winneschßdens eine vun dä Bovverfläsche uss_em [https://www.mediawiki.org/wiki/Category:All_skins Verzeischneß vun de Bedehnbovverfläsche vum MeedijaWikki] enschtallehre. Dat jeihd, endämm dat De:\n*:* einzel veröffentleschte Bovverfläsche us [https://www.mediawiki.org/wiki/Special:SkinDistributor MediaWiki.org] erongerlähds un en et Verzeischneß <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">skins/</code> vun dä MeedijaWikki_Enschtallazuhn holls,\n*:* winneschsdens eins vun dä Verzeischneße us <code lang=\"en\" xml:lang=\"en\">mediawiki/skins/*</code> met <i lang=\"en\" xml:lang=\"en\">Git</i> en et Verzeischneß <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">skins/</code> vun Dinge MeedijaWikki_Enschtallazuhn holls,\n*:* de [https://www.mediawiki.org/wiki/Download Dattei vum MeedijaWikki] erongerlähds, woh ongerscheidlejje Bedehnbovverfläsche dren sin un Zohsäz derzoh. Uß däm Verzeischneß doh dren kam_mer Saache en et Verzeischneß <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">skins/</code> vun dä MeedijaWikki_Enschtallazuhn holle.\n*: Dat sullt sesch nit met Dingem <i lang=\"en\" xml:lang=\"en\">git</i>-Verzeischneß schtühre, falls De och ene Äntweckler vum MeedijaWikki bes.\n*'''Falls dat MeedijaWikki heh jrahd obb ene neue Schtand jebraht wood:'''\n*: Bei MeedijaWikki en dä Väsjohn 1.24 un hüüter wääde de enschtallehrte Bedehnbovverfläsche nit mieh automattesch alle aanjemaat; süsch och em [https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery Handbohch] derhoh. Do kanns heh di Reihje e de Dattei <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">LocalSettings.php</code> eren koppehre, öm alle enschtallehrte Bedehnbovverfläsche aanzemaache:\n*:<pre lang=\"en\" xml:lang=\"en\" dir=\"ltr\">$3</pre>\n* '''Falls de jrahd aan dä Dattei <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">LocalSettings.php</code> jät geändert häs:'''\n*: Donn de Nahme vun de Bedehnbovverfläsche en dä Dattei pröhve. Se künnte verhehrt jeschrevve sin.",
-       "default-skin-not-found-no-skins": "De schtandattmähßejje Bedehnbovverfläsch <code>$1</code> för et Wikki es nit ze fenge. Se weed övver dä Enndraach <code lang=\"en\" xml:lang=\"en\">$wgDefaultSkin</code> en dä Dattei <code lang=\"en\" xml:lang=\"en\">LocalSettings.php</code> om ẞööver faßjelaat.\n\nEt sinn_er kein Bedehnbovverfläsche doh.\n\n*'''Falls dat heh e fresch enjereesch MeedijaWikki, es udder jrahd obb ene neue Schtand jebraht wood:'''\n*: MeedijaWikki wood velleisch övver <i lang=\"en\" xml:lang=\"en\">Git</i> eschtallehrt, udder der Quälltäx wood tiräk obb_en ander Manier eschtallehrt. Met däm Problehm heh wohr ze rääschne. Bei MeedijaWikki en dä Väsjohn 1.24 un hüüter sin kein Bedehnbovverfläsche mieh automattesch derbei. Donn winneschßdens eine vun dä Bovverfläsche uss_em [https://www.mediawiki.org/wiki/Category:All_skins Verzeischneß vun de Bedehnbovverfläsche] enschtallehre. Dat jeihd, endämm dat De:\n*:* winneschsdens eins vun dä Verzeischneße us <code lang=\"en\" xml:lang=\"en\">mediawiki/skins/*</code> met <i lang=\"en\" xml:lang=\"en\">Git</i> en et Verzeischneß <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">skins/</code> vun Dinge MeedijaWikki_Enschtallazuhn holls,\n*:* einzel veröffentleschte Bovverfläsche us [https://www.mediawiki.org/wiki/Special:SkinDistributor MediaWiki.org] erongerlähds un en et Verzeischneß <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">skins/</code> vun Dinge MeedijaWikki_Enschtallazuhn holls,\n*:* de [https://www.mediawiki.org/wiki/Download Dattei vum MeedijaWikki] erongerlähds, woh ongerscheidlejje Bedehnbovverfläsche dren sin un Zohsäz dder derzoh. Uß däm Verzeischneß doh dren kam_mer Saache en et Verzeischneß <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">skins/</code> vun Dinge vun dä MeedijaWikki_Enschtallazuhn holle.\n*: Dat sullt sesch nit met Dingem <i lang=\"en\" xml:lang=\"en\">Git</i>-Verzeischneß schtühre, falls De och ene Äntweckler vum MeedijaWikki bes. Loor em [https://www.mediawiki.org/wiki/Manual:Skin_configuration Handbohch] derhoh, wi mer Bedehnbovverfläsche aanmääd un ene Schtandatt faßlähsch.",
+       "default-skin-not-found": "De schtandattmähßejje Bedehnbovverfläsch <code>$1</code> för et Wikki es nit ze fenge. Se weed övver dä Enndraach <code lang=\"en\" xml:lang=\"en\">$wgDefaultSkin</code> en dä Dattei <code lang=\"en\" xml:lang=\"en\">LocalSettings.php</code> om ẞööver faßjelaat.\n\n{{PLURAL:$4|Heh di Bedehnbovverfläsch es|Heh di Bedehnbovverfläsche sin|ein Bedehnbovverfläsche sin}} doh:\n\n$2\n\nLohr och en et [https://www.mediawiki.org/wiki/Manual:Skin_configuration/de Handbohch övver et Enschtälle vun Bedehnbovverfläsche].\n\n*'''Falls dat heh e fresch enjereesch MehdijaWikki es:'''\n*: MehdijaWikki wood velleisch övver <i lang=\"en\" xml:lang=\"en\">Git</i> enschtallehrt, udder der Quälltäx wood tiräk obb_en ander Manier enschtallehrt. Met däm Problehm heh wohr ze rääschne. Donn winneschßdens eine vun dä Bovverfläsche uss_em [https://www.mediawiki.org/wiki/Category:All_skins Verzeischneß vun de Bedehnbovverfläsche vum MehdijaWikki] enschtallehre. Dat jeihd, endämm dat De:\n*:* einzel veröffentleschte Bovverfläsche us [https://www.mediawiki.org/wiki/Special:SkinDistributor MediaWiki.org] erongerlähds un en et Verzeischneß <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">skins/</code> vun dä MehdijaWikki_Enschtallazuhn holls,\n*:* winneschsdens eins vun dä Verzeischneße us <code lang=\"en\" xml:lang=\"en\">mediawiki/skins/*</code> met <i lang=\"en\" xml:lang=\"en\">Git</i> en et Verzeischneß <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">skins/</code> vun Dinge MehdijaWikki_Enschtallazuhn holls,\n*:* de [https://www.mediawiki.org/wiki/Download Dattei vum MehdijaWikki] erongerlähds, woh ongerscheidlejje Bedehnbovverfläsche dren sin un Zohsäz derzoh. Uß däm Verzeischneß doh dren kam_mer Saache en et Verzeischneß <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">skins/</code> vun dä MehdijaWikki_Enschtallazuhn holle.\n*: Dat sullt sesch nit met Dingem <i lang=\"en\" xml:lang=\"en\">git</i>-Verzeischneß schtühre, falls De och ene Äntweckler vum MehdijaWikki bes.\n*'''Falls dat MehdijaWikki heh jrahd obb ene neue Schtand jebraht wood:'''\n*: Bei MehdijaWikki en dä Väsjohn 1.24 un hüüter wääde de enschtallehrte Bedehnbovverfläsche nit mieh automattesch alle aanjemaat; süsch och em [https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery Handbohch] dernoh. Do kanns heh di {{PLURAL:$5|Reih|Reihje|kein Reihje}} en de Dattei <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">LocalSettings.php</code> eren koppehre, öm alle enschtallehrte Bedehnbovverfläsche aanzeschallde:\n<pre lang=\"en\" xml:lang=\"en\" dir=\"ltr\">$3</pre>\n* '''Falls de jrahd aan dä Dattei <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">LocalSettings.php</code> jät geändert häs:'''\n*: Donn de Nahme vun de Bedehnbovverfläsche en dä Dattei pröhve. Se künnte verhehrt jeschrevve sin.",
+       "default-skin-not-found-no-skins": "De schtandattmähßejje Bedehnbovverfläsch <code>$1</code> för et Wikki es nit ze fenge. Se weed övver dä Enndraach <code lang=\"en\" xml:lang=\"en\">$wgDefaultSkin</code> en dä Dattei <code lang=\"en\" xml:lang=\"en\">LocalSettings.php</code> om ẞööver faßjelaat.\n\nEt sinn_er kein Bedehnbovverfläsche doh.\n\n*'''Falls dat heh e fresch enjereesch MehdijaWikki, es udder jrahd obb ene neue Schtand jebraht wood:'''\n*: MehdijaWikki wood velleisch övver <i lang=\"en\" xml:lang=\"en\">Git</i> enschtallehrt, udder der Quälltäx wood tiräk obb_en ander Manier enschtallehrt. Met däm Problehm heh wohr ze rääschne. Bei MehdijaWikki en dä Väsjohn 1.24 un hüüter sin kein Bedehnbovverfläsche mieh automattesch derbei. Donn winneschßdens eine vun dä Bovverfläsche uss_em [https://www.mediawiki.org/wiki/Category:All_skins Verzeischneß vun de Bedehnbovverfläsche] enschtallehre. Dat jeihd, endämm dat De:\n*:* winneschsdens eins vun dä Verzeischneße us <code lang=\"en\" xml:lang=\"en\">mediawiki/skins/*</code> met <i lang=\"en\" xml:lang=\"en\">Git</i> en et Verzeischneß <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">skins/</code> vun Dinge MehdijaWikki_Enschtallazuhn holls,\n*:* einzel veröffentleschte Bovverfläsche us [https://www.mediawiki.org/wiki/Special:SkinDistributor MediaWiki.org] erongerlähds un en et Verzeischneß <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">skins/</code> vun Dinge MehdijaWikki_Enschtallazuhn holls,\n*:* de [https://www.mediawiki.org/wiki/Download Dattei vum MehdijaWikki] erongerlähds, woh ongerscheidlejje Bedehnbovverfläsche dren sin un Zohsäz derzoh. Uß däm Verzeischneß doh dren kam_mer Saache en et Verzeischneß <code lang=\"en\" xml:lang=\"en\" dir=\"ltr\">skins/</code> vun Dinge vun dä MehdijaWikki_Enschtallazuhn holle.\n*: Dat sullt sesch nit met Dingem <i lang=\"en\" xml:lang=\"en\">Git</i>-Verzeischneß schtühre, falls De och ene Äntweckler vum MehdijaWikki bes. Loor em [https://www.mediawiki.org/wiki/Manual:Skin_configuration Handbohch] dernoh, wi mer Bedehnbovverfläsche aanmääd un ene Schtandatt faßlähsch.",
        "default-skin-not-found-row-enabled": "* <code>$1</code> / $2 (enjeschalldt)",
        "default-skin-not-found-row-disabled": "* <code>$1</code> / $2 ('''ußjeschalldt''')",
        "mediastatistics": "Schtateßteke övver de Meedije",
index e19bf7b..7fbec56 100644 (file)
@@ -29,7 +29,8 @@
                        "לערי ריינהארט",
                        "Vogone",
                        "아라",
-                       "Aswanas"
+                       "Aswanas",
+                       "Pofka"
                ]
        },
        "tog-underline": "Pabraukti nuorodas:",
        "disclaimers": "Atsakomybės apribojimas",
        "disclaimerpage": "Project:Atsakomybės apribojimas",
        "edithelp": "Kaip redaguoti",
+       "helppage-top-gethelp": "Pagalba",
        "mainpage": "Pagrindinis puslapis",
        "mainpage-description": "Pagrindinis puslapis",
        "policy-url": "Project:Politika",
        "thumbnail_gd-library": "Nepilna GD bibliotekos konfigūracija: trūksta funkcijos $1",
        "thumbnail_image-missing": "Gali būti, kad failo nėra: $1",
        "import": "Importuoti puslapius",
-       "importinterwiki": "Tarpprojektinis importas",
+       "importinterwiki": "Įkelti iš kitos Wiki",
        "import-interwiki-text": "Pasirinkite projektą ir puslapio pavadinimą importavimui.\nVersijų datos ir redaktorių vardai bus išlaikyti.\nVisi tarpprojektiniai importo veiksmai yra registruojami  [[Special:Log/import|importo istorijoje]].",
        "import-interwiki-sourcewiki": "Pradinė wiki:",
        "import-interwiki-sourcepage": "Pirminis puslapis:",
        "rightsnone": "(jokių)",
        "revdelete-summary": "keitimo paaiškinimas",
        "feedback-adding": "Pridedamas atsiliepimas į puslapį ...",
+       "feedback-back": "Atgal",
        "feedback-bugcheck": "Puiku! Tiesiog patikrinkite, ar tai ne viena [$1 jau žinomų klaidų].",
        "feedback-bugnew": "Patikrinau. Pranešti apie naują klaidą",
        "feedback-bugornote": "Jei jūs esate pasirengę aprašyti techninę problemą išsamiau, [$1 praneškite apie programinę klaidą].\nKitu atveju, galite naudotis žemiau esančia paprastesne forma. Jūsų komentaras bus įtrauktas į puslapį „[$3 $2]“, kartu su jūsų naudotojo vardu ir jūsų naudojama naršykle.",
        "feedback-cancel": "Atšaukti",
        "feedback-close": "Atlikta",
+       "feedback-dialog-title": "Pateikti atsiliepimą",
+       "feedback-error-title": "Klaida",
        "feedback-error1": "Klaida: Neatpažįstamas rezultatas iš API",
        "feedback-error2": "Klaida: Redagavimas nepavyko",
        "feedback-error3": "Klaida: Jokio atsakymo iš API",
        "feedback-message": "Pranešimas:",
        "feedback-subject": "Tema:",
-       "feedback-submit": "Siųsti Atsiliepimą",
+       "feedback-submit": "Pateikti",
        "feedback-thanks": "Ačiū! Jūsų atsiliepimas buvo užregistruotas puslapyje „[$2 $1]“.",
        "searchsuggest-search": "Ieškoti",
        "searchsuggest-containing": "turintys",
index f0d436a..d8d4836 100644 (file)
        "listfiles_size": "幅(位元組)",
        "listfiles_description": "述",
        "listfiles_count": "擇",
+       "listfiles-latestversion": "當前版本",
        "listfiles-latestversion-yes": "是",
        "listfiles-latestversion-no": "否",
        "file-anchor-link": "檔",
        "filedelete-reason-dropdown": "\n*常刪之因\n** 侵版權\n** 重檔",
        "filedelete-edit-reasonlist": "纂刪因",
        "filedelete-maintenance": "護當禁刪復檔也。",
+       "filedelete-maintenance-title": "無法刪檔",
        "mimesearch": "篩檔",
        "mimesearch-summary": "此頁可以MIME篩檔.格仿「文類/次類」,如<code>image/jpeg</code>。",
        "mimetype": "MIME類有:",
        "unusedtemplateswlh": "支鏈",
        "randompage": "清風翻書",
        "randompage-nopages": "下列{{PLURAL:$2|名集}}中無頁也:$1",
+       "randomincategory-category": "類:",
        "randomredirect": "任渡",
        "randomredirect-nopages": "「$1」名冊內無渡也。",
        "statistics": "彙統",
        "protectedpages": "頁錮",
        "protectedpages-indef": "只示無期之錮",
        "protectedpages-cascade": "只示連串之錮",
+       "protectedpages-timestamp": "時間戳",
        "protectedpages-page": "頁",
        "protectedpages-expiry": "屆期",
        "protectedpages-reason": "因:",
+       "protectedpages-unknown-timestamp": "未知",
        "listusers": "點簿",
        "listusers-editsonly": "只示有纂之簿",
        "listusers-creationsort": "按先後列之",
        "pager-older-n": "陳$1次",
        "suppress": "監",
        "querypage-disabled": "此奇頁基以效,故停之。",
+       "apihelp": "程式接口幫助檔",
        "booksources": "書海",
        "booksources-search-legend": "舀書海",
        "booksources-search": "尋",
        "activeusers-noresult": "無簿矣。",
        "listgrouprights": "權任一覽",
        "listgrouprights-summary": "此所列述,諸職所司也,各有異同。欲知其詳,請閱[[{{MediaWiki:Listgrouprights-helppage}}|此文]]。",
-       "listgrouprights-key": "* <span class=\"listgrouprights-granted\">權授矣</span>\n* <span class=\"listgrouprights-revoked\">權撤矣</span>",
+       "listgrouprights-key": "圖例:\n* <span class=\"listgrouprights-granted\">權授矣</span>\n* <span class=\"listgrouprights-revoked\">權撤矣</span>",
        "listgrouprights-group": "組",
        "listgrouprights-rights": "權",
        "listgrouprights-helppage": "Help:組權",
        "noemailtext": "此君無郵。",
        "nowikiemailtext": "此君謝收郵之。",
        "emailusername": "簿名:",
+       "emailusernamesubmit": "呈",
        "email-legend": "發郵至{{SITENAME}}之另一簿",
        "emailfrom": "自:",
        "emailto": "致:",
        "mywatchlist": "哨站",
        "watchlistfor2": "$1之哨 $2",
        "nowatchlist": "無哨",
-       "watchlistanontext": "$1以治哨",
+       "watchlistanontext": "登簿以治哨",
        "watchnologin": "未登簿",
        "addwatch": "增至哨站",
        "addedwatchtext": "\"[[:$1]]\"哨派矣。後有易、議者可見於[[Special:Watchlist|哨站]],且'''粗體'''列於[[Special:RecentChanges|近易]]。",
        "unblocked-id": "禁$1赦焉",
        "ipblocklist": "列禁簿",
        "ipblocklist-legend": "尋禁簿",
+       "blocklist-reason": "因:",
        "ipblocklist-submit": "尋",
        "ipblocklist-localblock": "本禁",
        "ipblocklist-otherblocks": "他{{PLURAL:$1|禁|禁}}",
        "unblocklink": "赦",
        "change-blocklink": "更",
        "contribslink": "勛",
+       "emaillink": "遣函",
        "autoblocker": "近日$1\"$2\";同子IP址,故禁焉。",
        "blocklogpage": "誌禁",
        "blocklog-showlog": "此簿曾被禁。誌禁示下:",
        "import-invalid-interwiki": "無乃定之wiki匯入。",
        "importlogpage": "誌入",
        "importlogpagetext": "秩入匯自他山之審。",
-       "import-logentry-upload": "[[$1]]上傳而匯",
        "import-logentry-upload-detail": "有審$1",
-       "import-logentry-interwiki": "互匯$1",
        "import-logentry-interwiki-detail": "$1審自$2",
        "tooltip-pt-userpage": "述平生、紹身家、銘字號",
        "tooltip-pt-anonuserpage": "君IP之舍",
        "spam_reverting": "還新審之無鏈$1者。",
        "spam_blanking": "審皆鏈$1,遂令白頁。",
        "pageinfo-header-restrictions": "頁錮",
+       "pageinfo-robot-index": "可",
+       "pageinfo-robot-noindex": "不可",
        "pageinfo-toolboxlink": "文訊",
        "markaspatrolleddiff": "派哨",
        "markaspatrolledtext": "哨此報",
        "bydate": "時序",
        "sp-newimages-showfrom": "自$1 $2賞新檔",
        "video-dims": "$1,$2矩$3",
+       "just-now": "方",
+       "hours-ago": "$1時前",
+       "yesterday-at": "昨日於 $1",
        "bad_image_list": "僅取表件,冠「*」者也。格式如下:\n\n單列數鏈,首通壞檔;而後所鏈之文,允圖見於其內。",
        "metadata": "補註",
        "metadata-help": "此檔補註,製者所添,如相機、掃描之器;後若更檔,補註不誠也。",
index 2c424ba..6a285b3 100644 (file)
        "import-rootpage-nosubpage": "Именскиот простор „$1“ на основната страница не допушта потстраници.",
        "importlogpage": "Дневник на увезувања",
        "importlogpagetext": "Административно увезување на страници со историја на уредување од други викија.",
-       "import-logentry-upload": "увезена [[$1]] со подигање на податотека",
        "import-logentry-upload-detail": "{{PLURAL:$1|Увезена е една преработка|Увезени се $1 преработки}}",
-       "import-logentry-interwiki": "трансвикифиран $1",
        "import-logentry-interwiki-detail": "{{PLURAL:$1|Увезена е една преработка|Увезени се $1 преработки}} од $2",
        "javascripttest": "Проба на JavaScript",
        "javascripttest-pagetext-noframework": "Оваа страница е резервирана за вршење на проби со JavaScript.",
        "logentry-block-reblock": "$1 {{GENDER:$2|го измени}} блокот на {{GENDER:$4|$3}} со истек $5 $6",
        "logentry-suppress-block": "$1 {{GENDER:$2|го блокираше}} {{GENDER:$4|$3}} со истек $5 $6",
        "logentry-suppress-reblock": "$1 {{GENDER:$2|го измени}} блокот на {{GENDER:$4|$3}} со истек $5 $6",
+       "logentry-import-upload": "$1 {{GENDER:$2|ја увезе}} $3 со податотечно подигање",
+       "logentry-import-interwiki": "$1 {{GENDER:$2|ја увезе}} $3 од друго вики",
        "logentry-merge-merge": "$1 {{GENDER:$2|ја припои}} $3 кон $4 (преработки сè до $5)",
        "logentry-move-move": "$1 {{GENDER:$2|ја премести}} страницата $3 на $4",
        "logentry-move-move-noredirect": "$1 {{GENDER:$2|ја премести}} страницата $3 на $4 без да остави пренасочување",
index 8b8f57a..b440cbd 100644 (file)
        "tags-create-reason": "കാരണം:",
        "tags-create-submit": "സൃഷ്ടിക്കുക",
        "tags-create-no-name": "റ്റാഗിന്റെ പേര് വ്യക്തമാക്കേണ്ടതുണ്ട്.",
+       "tags-create-warnings-above": "\"$1\" എന്ന ടാഗ് സൃഷ്ടിക്കാൻ ശ്രമിക്കുമ്പോൾ താഴെക്കൊടുത്തിരിക്കുന്ന {{PLURAL:$2|മുന്നറിയിപ്പ്|മുന്നറിയിപ്പുകൾ}} വന്നു:",
        "tags-delete-reason": "കാരണം:",
        "tags-activate-reason": "കാരണം:",
        "tags-activate-submit": "സജ്ജമാക്കുക",
        "logentry-newusers-create2": "$3 എന്ന ഉപയോക്തൃ അംഗത്വം $1 {{GENDER:$2|സൃഷ്ടിച്ചിരിക്കുന്നു}}",
        "logentry-newusers-byemail": "$3 എന്ന ഉപയോക്തൃ അംഗത്വം $1 {{GENDER:$2|സൃഷ്ടിച്ചിരിക്കുന്നു}}, രഹസ്യവാക്ക് ഇമെയിൽ വഴി അയച്ചു",
        "logentry-newusers-autocreate": "$1 എന്ന ഉപയോക്തൃ അംഗത്വം സ്വയം {{GENDER:$2|സൃഷ്ടിക്കപ്പെട്ടിരിക്കുന്നു}}",
-       "logentry-rights-rights": "$3 à´\8eà´¨àµ\8dà´¨ à´\89പയàµ\8bà´\95àµ\8dതാവിനàµ\8dà´±àµ\86 à´¸à´\82à´\98 à´\85à´\82à´\97à´¤àµ\8dà´µà´\82, $4 à´\8eà´¨àµ\8dനതിൽ à´¨à´¿à´¨àµ\8dà´¨àµ\81 $5 à´\8eà´¨àµ\8dനതിലàµ\87à´\95àµ\8dà´\95àµ\81, $1 {{GENDER:$2|മാറ്റിയിരിക്കുന്നു}}",
+       "logentry-rights-rights": "$3 à´\8eà´¨àµ\8dà´¨ à´\89പയàµ\8bà´\95àµ\8dതാവിനàµ\8dà´±àµ\86 à´¸à´\82à´\98 à´\85à´\82à´\97à´¤àµ\8dà´µà´\82, $4 à´\8eà´¨àµ\8dനതിൽ à´¨à´¿à´¨àµ\8dà´¨àµ\81 $5 à´\8eà´¨àµ\8dനതിലàµ\87à´\95àµ\8dà´\95àµ\8d, $1 {{GENDER:$2|മാറ്റിയിരിക്കുന്നു}}",
        "logentry-rights-rights-legacy": "$3 എന്ന ഉപയോക്താവിന്റെ സംഘ അംഗത്വം $1 {{GENDER:$2|മാറ്റിയിരിക്കുന്നു}}",
        "logentry-rights-autopromote": "$1 എന്ന ഉപയോക്താവ് $4 എന്നതിൽ നിന്നും $5 എന്നതിലേയ്ക്ക് സ്വയമേവ {{GENDER:$2|ഉയർത്തപ്പെട്ടിരിക്കുന്നു}}",
        "logentry-upload-upload": "$1 $3 {{GENDER:$2|അപ്‌ലോഡ് ചെയ്തു}}",
index 742d123..2ae4343 100644 (file)
@@ -44,7 +44,8 @@
                        "संतोष दहिवळ",
                        "아라",
                        "BPositive",
-                       "Darshan kandolkar"
+                       "Darshan kandolkar",
+                       "Steinsplitter"
                ]
        },
        "tog-underline": "दुव्यांचे अधोरेखन:",
        "disclaimers": "उत्तरदायित्वास नकार",
        "disclaimerpage": "Project: सर्वसाधारण उत्तरदायकत्वास नकार",
        "edithelp": "संपादन साहाय्य",
-       "mainpage": "मुखपृष्ठ",
+       "mainpage": "'मुखपृष्ठ",
        "mainpage-description": "मुखपृष्ठ",
        "policy-url": "Project:नीती",
        "portal": "समाज मुखपृष्ठ",
index ad17a83..2a70e08 100644 (file)
        "import-rootpage-nosubpage": "'O namespace \"$1\" d' 'a pàggena princepale nun permette 'e sottopaggene.",
        "importlogpage": "Riggistro 'e mpurtaziune",
        "importlogpagetext": "Mpurtaziune ammenistrative 'e paggene c' 'a storia 'e cagnamiente 'e l'ati wiki.",
-       "import-logentry-upload": "ha mpurtato [[$1]] trammeto upload",
        "import-logentry-upload-detail": "$1 {{PLURAL:$1|verzione|verziune}} mpurtate",
-       "import-logentry-interwiki": "trasferito 'a n'ata wiki 'a paggena $1",
        "import-logentry-interwiki-detail": "$1 {{PLURAL:$1|verzione|verziune}} mpurtate 'a $2",
        "javascripttest": "Test JavaScript",
        "javascripttest-pagetext-noframework": "Sta paggena è riservata pe' l'esecuziune d' 'e test 'e Javascript.",
        "logentry-block-reblock": "$1 {{GENDER:$2|ave cagnato}} 'e mpustaziune 'e blocco pe' {{GENDER:$4|$3}} cu na scadenza 'e $5 $6",
        "logentry-suppress-block": "$1 {{GENDER:$2|ave bluccato}} {{GENDER:$4|$3}} cu na scadenza 'e $5 $6",
        "logentry-suppress-reblock": "$1 {{GENDER:$2|cagnaje}} 'e mpustaziune 'e blocco 'e {{GENDER:$4|$3}} ch' 'ammaturasse a nu $5 $6",
+       "logentry-import-upload": "$1 {{GENDER:$2|ave mpurtato}} $3 pe' bbìa d' 'a carreca",
+       "logentry-import-interwiki": "$1 {{GENDER:$2|ave mpurtato}} $3 pe' bbìa 'e n'atu wiki",
        "logentry-merge-merge": "$1 {{GENDER:$2|aunito|aunita}} $3 int' 'a $4 (verziune nzin' 'a $5)",
        "logentry-move-move": "$1 {{GENDER:$2|muvette}} paggena $3 a $4",
        "logentry-move-move-noredirect": "$1 {{GENDER:$2|muvette}} paggena $3 a $4 senza lassà nu redirezionamiento",
index a627e48..807711c 100644 (file)
@@ -14,7 +14,8 @@
                        "Reedy",
                        "ne.wikipedia.org sysops",
                        "सरोज कुमार ढकाल",
-                       "아라"
+                       "아라",
+                       "Steinsplitter"
                ]
        },
        "tog-underline": "सम्बन्ध निम्न रेखाङ्कन:",
        "disclaimers": "अस्विकारोक्तिहरु",
        "disclaimerpage": "Project:सामान्य अस्वीकारोक्ति",
        "edithelp": "सम्पादन सहायता",
-       "mainpage": "मुख्य पृष्ठ",
-       "mainpage-description": "मुख्य पृष्ठ",
+       "mainpage": "'गृह पृष्ठ",
+       "mainpage-description": "'गृह पृष्ठ",
        "policy-url": "Project:निति",
        "portal": "सामाजिक पोर्टल",
        "portal-url": "Project:सामाजिक पोर्टल",
        "import-invalid-interwiki": "खुलाइएको विकिबाट आयात गर्न सकिएन",
        "importlogpage": "आयात सूची",
        "import-logentry-upload-detail": "$1 {{PLURAL:$1|संशोधन|संशोधनहरु}}",
-       "import-logentry-interwiki": " $1लाई अन्तरविकिकरण गरियो",
        "import-logentry-interwiki-detail": "$2 देखि $1 {{PLURAL:$1|पुनरावलोकन|पुनरावलोकनहरु}}",
        "tooltip-pt-userpage": "तपाईको प्रयोगकर्ता पृष्ठ",
        "tooltip-pt-anonuserpage": "तपाईले जुन IP ठेगानाको रुपमा सम्पादन गर्दै हुनुहुन्छ , त्यसको प्रयोगकर्ता पृष्ठ निम्न छ :",
index 90a46e8..ebe273d 100644 (file)
        "unusedimages": "Nieużywane pliki",
        "wantedcategories": "Brakujące kategorie",
        "wantedpages": "Najpotrzebniejsze strony",
+       "wantedpages-summary": "Najczęściej linkowane nieistniejące strony, z wyłączeniem stron, do których linkują wyłącznie przekierowania. Lista nieistniejących stron, do których linkują przekierowania, znajduje się na [[{{#special:BrokenRedirects}}]].",
        "wantedpages-badtitle": "Nieprawidłowy tytuł wśród wyników – $1",
        "wantedfiles": "Potrzebne pliki",
        "wantedfiletext-cat": "Następujące pliki są używane, ale nie istnieją. Pliki z obcych repozytoriów mogą być wymienione pomimo istnienia. Takie fałszywe wyniki zostaną <del>przekreślone</del>. Ponadto strony, które osadzają pliki, które nie istnieją, są wymienione w [[:$1]].",
index c3de600..2fa50f4 100644 (file)
        "import-rootpage-nosubpage": "Lë spassi nominal «$1» ëd la pàgina prinsipal a përmët pa dle sot-pagine.",
        "importlogpage": "Registr dj'amportassion",
        "importlogpagetext": "Amportassion aministrative ëd pàgine e ëd soa stòria da dj'àutre wiki.",
-       "import-logentry-upload": "a l'ha amportà [[$1]] con un càrich d'archivi",
        "import-logentry-upload-detail": "$1 {{PLURAL:$1|revision}} amportà",
-       "import-logentry-interwiki": "Amportà da n'àutra wiki $1",
        "import-logentry-interwiki-detail": "$1 {{PLURAL:$1|revision}} amportà da $2",
        "javascripttest": "Preuva ëd JavaScript",
        "javascripttest-pagetext-noframework": "Costa pàgina a l'é arservà për fé dle preuve JavaScript.",
        "logentry-block-reblock": "$1 {{GENDER:$2|a l'ha modificà}} ij paràmeter ëd blocagi për {{GENDER:$4|$3}} con na durà ëd $5 $6",
        "logentry-suppress-block": "$1 {{GENDER:$2|a l'ha blocà}} {{GENDER:$4|$3}} për na durà ëd $5 $6",
        "logentry-suppress-reblock": "$1 {{GENDER:$2|a l'ha modificà}} ij paràmeter ëd blocagi për {{GENDER:$4|$3}} con na durà ëd $5 $6",
+       "logentry-import-upload": "$1 {{GENDER:$2|a l'ha amportà}} $3 an cariand d'archivi",
+       "logentry-import-interwiki": "$1 {{GENDER:$2|a l'ha amportà}} $3 da n'àutra wiki",
        "logentry-merge-merge": "$1 {{GENDER:$2|a l'ha gionzù}} $3 an $4 (revision fin-a a $5)",
        "logentry-move-move": "$1 {{GENDER:$2|a l'ha tramudà}} la pàgina $3 a $4",
        "logentry-move-move-noredirect": "$1 {{GENDER:$2|a l'ha tramudà}} la pàgina $3 a $4 sensa lassé na ridiression",
index 6ac333c..1484107 100644 (file)
        "editingsection": "This message displays at the top of the page when a user is editing a page section. Parameters:\n* $1 - page name\n{{Related|Editing}}",
        "editingcomment": "This message displays at the top of the page when a user is creating a new section. Parameters:\n* $1 - page name\n{{Related|Editing}}",
        "editconflict": "Used as title of error message. Parameters:\n* $1 - page title",
+       "editnotice-notext": "{{ignored}}\nCustom message on top of the edit page if no edit notices apply to this page.",
        "explainconflict": "Appears at the top of a page when there is an edit conflict.\n\nSee also:\n* {{msg-mw|Savearticle}}",
        "yourtext": "Used in Diff Preview page. The diff is between {{msg-mw|currentrev}} and {{msg-mw|yourtext}}.\n\nAlso used in Edit Conflict page; the diff between {{msg-mw|yourtext}} and {{msg-mw|storedversion}}.",
        "storedversion": "This is used in an edit conflict as the label for the top revision that has been stored, as opposed to your version {{msg-mw|yourtext}} that has not been stored which is shown at the bottom of the page.",
index 0cd6e56..35f8049 100644 (file)
        "pool-timeout": "Tiembe d'attese scadute pu 'u blocche",
        "pool-queuefull": "'A code de le sondagge jè chiene",
        "pool-errorunknown": "Errore scanusciute",
+       "poolcounter-usage-error": "Errore d'ause: $1",
        "aboutsite": "Sus a {{SITENAME}}",
        "aboutpage": "Project:Sus a",
        "copyright": "'U condenute jè disponibile sotte a $1.",
        "disclaimers": "No ne sacce ninde",
        "disclaimerpage": "Project:Scareca uarrile",
        "edithelp": "Cangianne l'ajute",
+       "helppage-top-gethelp": "Aijute",
        "mainpage": "Pàgene Prengepàle",
        "mainpage-description": "Pàgene Prengepàle",
        "policy-url": "Project:Reghele",
        "hidetoc": "scunne",
        "collapsible-collapse": "Scunne",
        "collapsible-expand": "Spanne",
+       "confirmable-confirm": "{{GENDER:$1|Sì}} secure?",
        "confirmable-yes": "Sìne",
        "confirmable-no": "None",
        "thisisdeleted": "Vide o ripristine $1?",
        "readonly_lag": "'U database ha state automaticamende blocchete purcè le server de le database ca depennene da 'u master onne sciute in eccezzione",
        "internalerror": "Errore inderne",
        "internalerror_info": "Errore inderne: $1",
+       "internalerror-fatal-exception": "Eccezzione fatale d'u tipe \"$1\"",
        "filecopyerror": "Non ge pozze cupià 'u fail \"$1\" jndr'à \"$2\".",
        "filerenameerror": "Non ge pozze cangià 'u nome d'u fail \"$1\" jndr'à \"$2\".",
        "filedeleteerror": "Non ge pozze scangillà 'u fail \"$1\".",
        "thumbnail_gd-library": "Configurazione d'a libbrerie GD ingomblete: funziona perse $1",
        "thumbnail_image-missing": "'U file pare ca non ge se iacchie: $1",
        "import": "Pàggene 'mbortete",
-       "importinterwiki": "'Mborte da Transuicchi",
-       "import-interwiki-text": "Schacchie 'na Uicchi e 'nu titele de pàgene da 'mbortà.\nLe date d'a revisione e 'u nome de le cangiature avènene preservate.\nTutte le aziune de 'mbortaziune 'mbrà le Uicchi sonde reggistrate jndr'à l'[[Special:Log/import|archivije de le 'mbortaziune]].",
+       "importinterwiki": "'Mborte da 'n'otra uicchi",
+       "import-interwiki-text": "Schacchie 'na Uicchi e 'nu titole de pàgene da 'mbortà.\nLe date d'a revisione e 'u nome de le cangiature avènene preservate.\nTutte le aziune de 'mbortaziune 'mbrà le Uicchi sonde reggistrate jndr'à l'[[Special:Log/import|archivije de le 'mbortaziune]].",
        "import-interwiki-history": "Copie tutte 'a sotrie de le versiune de sta pàgene",
        "import-interwiki-templates": "Inglude tutte le template",
        "import-interwiki-submit": "'Mborte",
        "import-rootpage-nosubpage": "Namespace \"$1\" d'a pàgene prengepàle non ge permette le sottopàggene.",
        "importlogpage": "Archivie de le 'mbortaziune",
        "importlogpagetext": "'Mbortaziune amministrative de pàggene cu 'a storie de le cangiaminde da otre Uicchi.",
-       "import-logentry-upload": "'mbortete [[$1]] da 'u fail carechete",
        "import-logentry-upload-detail": "$1 {{PLURAL:$1|revisione|revisiune}} 'mbortate",
-       "import-logentry-interwiki": "transuicchied $1",
        "import-logentry-interwiki-detail": "$1 {{PLURAL:$1|revisione|revisiune}} 'mbortate da $2",
        "javascripttest": "Test de JavaScript",
        "javascripttest-pagetext-noframework": "Sta pàgene jè riservate pe le esecuziune de le test de Javascript.",
index eb079cb..d6ef5dd 100644 (file)
        "import-rootpage-nosubpage": "Imenski prostor »$1« korenske strani ne dovoli podstrani.",
        "importlogpage": "Dnevnik uvozov",
        "importlogpagetext": "Administrativni uvozi strani z zgodovino urejanja iz drugih wikijev.",
-       "import-logentry-upload": "uvozil [[$1]] z nalaganjem datoteke",
        "import-logentry-upload-detail": "{{PLURAL:$1|Uvožena $1 redakcija|Uvoženi $1 redakciji|Uvožene $1 redakcije|Uvoženih $1 redakcij}}",
-       "import-logentry-interwiki": "prenesel $1 med wikiji",
        "import-logentry-interwiki-detail": "{{PLURAL:$1|Uvožena $1 redakcija|Uvoženi $1 redakciji|Uvožene $1 redakcije|Uvoženih $1 redakcij}} z $2",
        "javascripttest": "Preizkušanje JavaScripta",
        "javascripttest-pagetext-noframework": "Stran je rezervirana za poganjanje preizkusov JavaScript.",
        "logentry-block-reblock": "$1 je {{GENDER:$2|spremenil|spremenila|spremenil(-a)}} nastavitve blokade za {{GENDER:$4|$3}} s časomspremenil poteka $5 $6",
        "logentry-suppress-block": "$1 je {{GENDER:$2|blokiral|blokirala|blokiral(-a)}} {{GENDER:$4|$3}} s časom poteka $5 $6",
        "logentry-suppress-reblock": "$1 je {{GENDER:$2|spremenil|spremenila|spremenil(-a)}} nastavitve blokade za {{GENDER:$4|$3}} s časom poteka $5 $6",
+       "logentry-import-upload": "$1 je {{GENDER:$2|uvozil|uvozila|uvozil(-a)}} $3 z nalaganjem datoteke",
+       "logentry-import-interwiki": "$1 je {{GENDER:$2|uvozil|uvozila|uvozil(-a)}} $3 z drugega wikija",
        "logentry-merge-merge": "$1 je {{GENDER:$2|združil|združila|združil(-a)}} $3 z $4 (redakcije do $5)",
        "logentry-move-move": "$1 je {{GENDER:$2|premaknil|premaknila|premaknil(-a)}} stran $3 na $4",
        "logentry-move-move-noredirect": "$1 je {{GENDER:$2|prestavil|prestavila|prestavil(-a)}} stran $3 na $4 brez preusmeritve",
index 585b55b..688e526 100644 (file)
        "templatesused": "{{PLURAL:$1|Шаблон|Шаблони}} на овој страници:",
        "templatesusedpreview": "{{PLURAL:$1|Шаблон|Шаблони}} у овом прегледу:",
        "templatesusedsection": "{{PLURAL:$1|Шаблон|Шаблони}} у овом одељку:",
-       "template-protected": "(заштићен)",
-       "template-semiprotected": "(полузаштићен)",
+       "template-protected": "(заштићено)",
+       "template-semiprotected": "(полузаштићено)",
        "hiddencategories": "Ова страница је члан {{PLURAL:$1|једне скривене категорије|$1 скривене категорије|$1 скривених категорија}}:",
        "edittools": "<!-- Овај текст ће бити приказан испод обрасца за уређивање и отпремање. -->",
        "edittools-upload": "-",
        "logentry-block-reblock": "$1 је {{GENDER:$2|променио|променила}} подешавања за блокирање {{GENDER:$4|корисника|кориснице}} {{GENDER:$4|$3}} у трајању од $5 $6",
        "logentry-suppress-block": "$1 је {{GENDER:$2|блокирао|блокирала}} {{GENDER:$4|$3}} у трајању од $5 $6",
        "logentry-suppress-reblock": "$1 је {{GENDER:$2|променио|променила}} подешавања за блокирање {{GENDER:$4|корисника|кориснице}} {{GENDER:$4|$3}} у трајању од $5 $6",
+       "logentry-import-upload": "$1 је {{GENDER:$2|увезао|увезла}} $3 отпремањем датотеке",
+       "logentry-import-interwiki": "$1 је {{GENDER:$2|увезао|увезла}} $3 с другог викија",
        "logentry-merge-merge": "$1 је {{GENDER:$2|спојио|спојила}} $3 у $4 (све до измене $5)",
        "logentry-move-move": "$1 је {{GENDER:$2|преместио|преместила}} страницу $3 на $4",
        "logentry-move-move-noredirect": "$1 је {{GENDER:$2|преместио|преместила}} страницу $3 на $4 без остављања преусмерења",
index 1b7aacf..61b3176 100644 (file)
        "templatesused": "{{PLURAL:$1|Šablon|Šabloni}} na ovoj stranici:",
        "templatesusedpreview": "{{PLURAL:$1|Šablon|Šabloni}} u ovom pregledu:",
        "templatesusedsection": "{{PLURAL:$1|Šablon|Šabloni}} u ovom odeljku:",
-       "template-protected": "(zaštićen)",
-       "template-semiprotected": "(poluzaštićen)",
+       "template-protected": "(zaštićeno)",
+       "template-semiprotected": "(poluzaštićeno)",
        "hiddencategories": "Ova stranica je član {{PLURAL:$1|jedne skrivene kategorije|$1 skrivene kategorije|$1 skrivenih kategorija}}:",
        "edittools": "<!-- Ovaj tekst će biti prikazan ispod obrasca za uređivanje i otpremanje. -->",
        "edittools-upload": "-",
index 11260c4..21d3dbd 100644 (file)
        "logentry-block-reblock": "$1 {{GENDER:$2|ändrade}} blockeringsinställningar för {{GENDER:$4|$3}} med en varaktighet på $5 $6",
        "logentry-suppress-block": "$1 {{GENDER:$2|blockerade}} {{GENDER:$4|$3}} med en varaktighet på $5 $6",
        "logentry-suppress-reblock": "$1 {{GENDER:$2|ändrade}} blockeringsinställningar för {{GENDER:$4|$3}} med en varaktighet på $5 $6",
+       "logentry-import-upload": "$1 {{GENDER:$2|importerade}} $3 genom filuppladdning",
+       "logentry-import-interwiki": "$1 {{GENDER:$2|importerade}} $3 från en annan wiki",
        "logentry-merge-merge": "$1 {{GENDER:$2|slog ihop}} $3 i $4 (versioner t.o.m. $5)",
        "logentry-move-move": "$1 {{GENDER:$2|flyttade}} sidan $3 till $4",
        "logentry-move-move-noredirect": "$1 {{GENDER:$2|flyttade}} sidan $3 till $4 utan att lämna en omdirigering",
index fd30fd3..d696243 100644 (file)
        "actions": "ಕ್ರಿಯೆಕ್ಕುಲು",
        "namespaces": "ಪುದರ್ ದ ವರ್ಗೊಲು",
        "variants": "ರೂಪಾಂತರ ಹೊಂದ್‘ನ",
+       "navigation-heading": "ಸಂಚರಣೆ ಮೆನು",
        "errorpagetitle": "ದೋಷ",
        "returnto": "$1 ಗ್ ಪಿರ ಪೋಲೆ.",
        "tagline": "{{SITENAME}} ರ್ದ್",
        "permalink": "ಸ್ಥಿರ ಸಂಪರ್ಕ",
        "print": "ಪ್ರಿ೦ಟ್ ಮನ್ಪುಲೆ",
        "view": "ತೂಲೆ",
+       "view-foreign": "$1ಡ್ ತೂಲೆ",
        "edit": "ಸಂಪಾದನೆ ಮಲ್ಪುಲೆ(Edit this page)",
        "create": "ಸೃಷ್ಟಿಸಾಲೆ",
        "editthispage": "ಈ ಪುಟೊನು ಬದಲಾಯಿಸಾಲೆ",
        "articlepage": "ಲೇಖನ ಪುಟೊನು ತೂಲೆ",
        "talk": "ಚರ್ಚೆ",
        "views": "ನೋಟಲು",
-       "toolbox": "ಉಪಕರಣ(ಟೂಲ್)",
+       "toolbox": "ಉಪಕರಣೊಲು",
        "userpage": "ಸದಸ್ಯೆರ್ನ ಪುಟೊನು ತೂಲೆ",
        "projectpage": "ಪ್ರೊಜೆಕ್ಟ್ ಪುಟೊನು ತೂಲೆ",
        "imagepage": "ಮೀಡಿಯ ಪುಟೊನು ತೂಲೆ",
        "login-abort-generic": "ಇರೆನ ಲಾಗ್ ಇನ್ ವಿಫಲ ವಾತ್‘ಂಡ್",
        "loginlanguagelabel": "ಭಾಷೆ: $1",
        "pt-login": "ಲಾಗಿನ್",
+       "pt-createaccount": "ಪೊಸ ಖಾತೆ ಸುರು ಮಲ್ಪುಲೆ",
        "changepassword": "ಪ್ರವೇಶಪದೊನ್ ಬದಲಾವಣೆ ಮಲ್ಪುಲೆ",
        "resetpass_header": "ಈ ಖಾತೆದ ಪ್ರವೇಶಪದ ಬದಲಾವಣೆ ಮಲ್ಪುಲೆ",
        "oldpassword": "ಪರ ಪ್ರವೇಶಪದ",
        "action-deleterevision": "ಈ ಆವೃತ್ತಿನ್ ಮಾಜಾಲೆ",
        "action-sendemail": "ಇ-ಅಂಚೆ ಕಡಪುಡುಲೆ",
        "nchanges": "$1 {{PLURAL:$1|ಬದಲಾವಣೆ|ಬದಲಾವಣೆಲು}}",
+       "enhancedrc-history": "ಇತಿಹಾಸೊ",
        "recentchanges": "ಇಂಚಿಪದ ಬದಲಾವಣೆಲು",
        "recentchanges-legend": "ಇಂಚಿಪದ ಬದಲಾವಣೆಲು ಆಯ್ಕೆಲು",
        "recentchanges-summary": "ಈ ವಿಕಿಟ್ ಇಂಚಿಪ್ಪ ಆತಿನ ಬದಲಾವಣೆನ್ ಟ್ರಾಕ್ ಮಲ್ಪುಲೆ",
        "recentchanges-label-minor": "ಉಂದು ಎಲ್ಯ ಬದಲಾವಣೆ",
        "rclistfrom": "$3 $2 ರ್ದ್ ಶುರುವಾತಿನ ಪೊಸ ಬದಲಾವಣೆಲೆನ್ ತೊಜ್ಪಾವು",
        "rcshowhideminor": "$1 ಎಲ್ಯೆಲ್ಯ ಬದಲಾವಣೆಲು",
+       "rcshowhideminor-hide": "ದೆಂಗಾವು",
        "rcshowhidebots": "$1 ಬಾಟ್",
+       "rcshowhidebots-show": "ತೊಜಾವು",
        "rcshowhideliu": "ಲಾಗ್-ಇನ್ ಆತಿನಂಚಿನ ಸದಸ್ಯೆರ್ $1",
+       "rcshowhideliu-hide": "ದೆಂಗಾವು",
        "rcshowhideanons": "ಅನಾಮಧೇಯ ಸದಸ್ಯೆರ್ $1",
+       "rcshowhideanons-hide": "ದೆಂಗಾವು",
        "rcshowhidepatr": "$1 ಪರೀಕ್ಷಿಸಾದಿನ ಸಂಪಾದನೆಲು",
        "rcshowhidemine": "ಎನ್ನ ಸಂಪಾದನೆಲೆನ್ $1",
+       "rcshowhidemine-hide": "ದೆಂಗಾವು",
        "rclinks": "ದುಂಬುದ $2 ದಿನೊಲೆಡ್ ಮಲ್ತಿನ $1 ಕಡೆತ ಬದಲಾವಣೆಲೆನ್ ತೂಲೆ <br />$3",
        "diff": "ವ್ಯತ್ಯಾಸ",
        "hist": "ಇತಿಹಾಸ",
        "imagelinks": "ಫೈಲ್ ಲಿಂಕ್’ಲು",
        "linkstoimage": "ಈ ಫೈಲ್’ಗ್ ತಿರ್ತ್’ದ ಈ {{PLURAL:$1|ಪುಟ|$1 ಪುಟೊಲು}} ಲಿಂಕ್ ಕೊರ್ಪುಂಡು.",
        "sharedupload": "ಈ ಫೈಲ್’ನ್ ಮಸ್ತ್ ಜನ ಪಟ್ಟ್’ದುಲ್ಲೆರ್ ಅಂಚೆನೆ ಉಂದು ಮಸ್ತ್ ಪ್ರೊಜೆಕ್ಟ್’ಲೆಡ್ ಉಪಯೋಗಡುಪ್ಪು.",
+       "upload-disallowed-here": "ಈರ್ ಈ ಕಡತನ್ ಕುಡ ಬರೆವರೆ ಸಾದ್ಯ ಇಜ್ಜಿ.",
        "filedelete-comment": "ಕಾರಣ",
        "filedelete-submit": "ಮಾಜಾಲೆ",
        "randompage": "ಯಾದೃಚ್ಛಿಕ ಪುಟ",
        "namespace": "ನೇಮ್-ಸ್ಪೇಸ್:",
        "invert": "ಆಯ್ಕೆನ್ ತಿರ್ಗಾಲೆ",
        "blanknamespace": "(ಮುಖ್ಯ)",
-       "contributions": "ಸದಸ್ಯೆರ್ನ ಕಾಣಿಕೆಲು",
+       "contributions": "{{$1ಸದಸ್ಯೆರ್ನ}} ಕಾಣಿಕೆಲು",
        "contributions-title": "$1 ಗ್ ಸದಸ್ಯೆರ್ನ ಕಾಣಿಕೆ",
        "mycontris": "ಎನ್ನ ಕಾಣಿಕೆಲು",
        "contribsub2": "$1 ($2) ಗ್",
        "tooltip-pt-mycontris": "ಎನ್ನ ಕಾಣಿಕೆಲ್ದ ಪಟ್ಟಿ",
        "tooltip-pt-login": "ಈರ್ ಲಾಗ್ ಇನ್ ಆವೊಡುಂದು ಕೋರೊಂದುಲ್ಲ, ಆಂಡ ಉಂದು ದಾಲ ಕಡ್ಡಾಯ ಅತ್ತ್.",
        "tooltip-pt-logout": "ಲಾಗ್ ಔಟ್",
+       "tooltip-pt-createaccount": "ನಿಕುಲು ಪೊಸ ಖಾತೆ ಸುರುಮಲ್ತ್‍ದ್ ಲಾಗಿನ್ ಆಪುನೈನ್ ಸ್ವಾಗತ ಮಲ್ಪುವೊ, ಆಂಡಲಾ ಉಂದು ಕಡ್ಡಾಯ ಅತ್ತ್.",
        "tooltip-ca-talk": "ಮಾಹಿತಿ ಪುಟೊತ ಬಗ್ಗೆ ಚರ್ಚೆ",
        "tooltip-ca-edit": "ಈ ಪುಟೊನು ಈರ್ ಸಂಪಾದನೆ ಮಲ್ಪೊಲಿ. ಸೇವ್ ಮಲ್ಪುನ ದುಂಬು ಮುನ್ನೋಟದ ಉಪಯೊಗ ಮನ್ತೊನ್ಲೆ.",
        "tooltip-ca-addsection": "ಪೊಸ ಸೆಶನ್ನ್ ಶರು ಮಲ್ಪುಲೆ",
        "tooltip-upload": "ಅಪ್ಲೋಡ್ ಸುರು ಮಲ್ಪು",
        "tooltip-rollback": "\"Rollback\", ಈ ಪುಟದ ಕರಿನ ಬದಾಲವಣೆಗ್ ಒ೦ಜಿ ಕ್ಲಿಕ್ ಡ್ ಕೊನೊಪು೦ಡು",
        "tooltip-undo": "\"Undo\" ಈ ಬದಲಾವಣೆನ್ ದೆತೊನುಜಿ ಬುಕ ಪ್ರಿವ್ಯೂ ಮೋಡ್ ಡ್ ಬದಲಾವಣೆ ಮಲ್ಪೆರ್ ಕೊನೊಪು೦ಡು. ಅ೦ಚೆನೆ ಸಮ್ಮರಿ ಡ್ ಬದಲಾವಣೆ ಗ್ ಕಾರಣ ಕೊರ್ರ್‍ಎ ಆಪು೦ಡು.",
+       "tooltip-summary": "ಒಂಜಿ ಎಲ್ಯ ಸಾರಾಂಶ ಕೊರ್ಲೆ",
+       "pageinfo-toolboxlink": "ಪುಟೊತ ಮಾಹಿತಿ",
        "previousdiff": "← ದುಂಬುದ ಸಂಪಾದನೆ",
        "nextdiff": "ಪೊಸ ಎಡಿಟ್ →",
        "file-info-size": "$1 × $2 ಪಿಕ್ಸೆಲ್, ಫೈಲ್’ದ ಗಾತ್ರ: $3, MIME ಪ್ರಕಾರ: $4",
        "file-nohires": "ಇಂದೆರ್ದ್ ಜಾಸ್ತಿ ವಿವರವಾಯಿನ ನೋಟ ಇಜ್ಜಿ.",
        "svg-long-desc": "ಎಸ್.ವಿ.ಜಿ ಫೈಲ್, ಸುಮಾರಾದ್ $1 × $2 ಪಿಕ್ಸೆಲ್, ಫೈಲ್’ದ ಗಾತ್ರ: $3",
-       "show-big-image": "ಪೂರ್ತಿ ರೆಸೊಲ್ಯೂಶನ್",
+       "show-big-image": "ಮೂಲ ಕಡತ",
+       "show-big-image-size": "$1 × $2 ಪಿಕ್ಸೆಲ್‌ಸ್",
        "bad_image_list": "ವ್ಯವಸ್ಥೆದ ಆಕಾರ ಈ ರೀತಿ ಉಂಡು:\n\nಪಟ್ಟಿಡುಪ್ಪುನಂಚಿನ ದಾಖಲೆಲೆನ್ (* ರ್ದ್ ಶುರು ಆಪುನ ಸಾಲ್’ಲು) ಮಾತ್ರ ಪರಿಗಣನೆಗ್ ದೆತೊನೆರಾಪುಂಡು.\nಪ್ರತಿ ಸಾಲ್’ದ ಶುರುತ ಲಿಂಕ್ ಒಂಜಿ ದೋಷ ಉಪ್ಪುನಂಚಿನ ಫೈಲ್’ಗ್ ಲಿಂಕಾದುಪ್ಪೊಡು.\nಅವ್ವೇ ಸಾಲ್’ದ ಶುರುತ ಪೂರಾ ಲಿಂಕ್’ಲೆನ್ ಪರಿಗನೆರ್ದ್ ದೆಪ್ಪೆರಾಪುಂಡು, ಪಂಡ ಓವು ಪುಟೊಲೆಡ್ ಫೈಲ್’ದ ಬಗ್ಗೆ ಬರ್ಪುಂಡೋ ಔಲು.",
        "metadata": "ಮೇಲ್ದರ್ಜೆ ಮಾಹಿತಿ",
        "metadata-help": "ಈ ಫೈಲ್’ಡ್ ಜಾಸ್ತಿ ಮಾಹಿತಿ ಉಂಡು. ಪ್ರಾಯಶಃ ಫೈಲ್’ನ್ ಉಂಡು ಮಲ್ಪೆರೆ ಉಪಯೋಗ ಮಲ್ತಿನ ಡಿಜಿಟಲ್ ಕ್ಯಾಮೆರರ್ದ್ ಅತ್ತ್’ನ್ಡ ಸ್ಕ್ಯಾನರ್ ರ್ದ್ ಈ ಮಾಹಿತಿ ಬೈದ್’ನ್ಡ್.\nಮೂಲಪ್ರತಿರ್ದ್ ಈ ಫೈಲ್ ಬದಲಾದಿತ್ತ್’ನ್ಡ, ಈ ಮಾಹಿತಿ ಬದಲಾತಿನ ಫೈಲ್’ದ ವಿವರೊಲೆಗ್ ಸರಿಯಾದ್ ಹೊಂದಂದೆ ಉಪ್ಪು.",
        "metadata-expand": "ವಿಸ್ತಾರವಾಯಿನ ವಿವರೊಲೆನ್ ತೊಜ್ಪಾವು",
        "metadata-collapse": "ವಿಸ್ತಾರವಾಯಿನ ವಿವರೊಲೆನ್ ದೆಂಗಾವು",
        "metadata-fields": "ಈ ಸಂದೇಶೊಡು ಪಟ್ಟಿ ಮಲ್ತಿನಂಚಿನ EXIF ಮೆಟಡೇಟ ಮಾಹಿತಿನ್ ಚಿತ್ರ ಪುಟೊಕು ಸೇರ್ಪಾಯೆರೆ ಆವೊಂದುಂಡು. ಪುಟೊಟು ಮೆಟಡೇಟ ಮಾಹಿತಿದ ಪಟ್ಟಿನ್ ದೆಪ್ಪುನಗ ಉಂದು ತೋಜುಂಡು.\nಒರಿದನವು ಮೂಲಸ್ಥಿತಿಟ್ ಅಗೋಚರವಾದುಪ್ಪುಂಡು.\n* make\n* model\n* datetimeoriginal\n* exposuretime\n* fnumber\n* isospeedratings\n* focallength\n* artist\n* copyright\n* imagedescription\n* gpslatitude\n* gpslongitude\n* gpsaltitude",
+       "exif-datetime": "ಕಡೊತೊನು ಬದಲಾವಣೆ ಮಲ್ತ್‍ನ ದಿನಾಂಕೊ ಬೊಕ್ಕ ಸಮಯೊ",
+       "exif-make": "ಕ್ಯಾಮೆರಾದ ತಯಾರೆಕೆರ್",
+       "exif-model": "ಕ್ಯಾಮೆರಾ ಮಾದರಿ",
+       "exif-software": "ಉಪಯೋಗ ಮಲ್ತಿನ ತಂತ್ರಾಂಶ",
+       "exif-exifversion": "Exif ಆವೃತ್ತಿ",
+       "exif-datetimeoriginal": "ಮಾಹಿತಿ ಸೃಷ್ಟಿಯಾಯಿನ ದಿನಾಂಕೊ ಬೊಕ್ಕ ಸಮಯ",
+       "exif-datetimedigitized": "ಗಣಕೀಕರಣದ ದಿನಾಂಕೊ ಬೊಕ್ಕ ಸಮಯೊ",
+       "exif-orientation-1": "ಸಾಧಾರಣ",
        "namespacesall": "ಪೂರಾ",
        "monthsall": "ಪೂರಾ",
        "watchlisttools-view": "ಪ್ರಸ್ತುತ ಬದಲಾವಣೆಲ್ ತೋಜಾಲೆ",
        "watchlisttools-edit": "ವೀಕ್ಷಣಾಪಟ್ಟಿನ್ ತೂಲೆ ಬೊಕ್ಕ ಎಡಿಟ್ ಮಲ್ಪುಲೆ",
        "specialpages": "ವಿಷೇಶ ಪುಟೊಲು",
+       "logentry-delete-delete": "$1 {{GENDER:$2|ಮಾಜಾದ್‍ಂಡ್}} ಪುಟ $3",
        "searchsuggest-search": "ನಾಡ್‍ಲೆ"
 }
index 98dc382..48aab34 100644 (file)
        "mypage": "Бит",
        "mytalk": "Бәхәс бите",
        "anontalk": "Бу IP адресы өчен бәхәс бите",
-       "navigation": "Ð\9aÒ¯Ñ\87Ò¯",
+       "navigation": "Ð\9dавигаÑ\86иÑ\8f",
        "and": "&#32;һәм",
        "qbfind": "Эзләү",
        "qbbrowse": "Карау",
        "history": "Битнең тарихы",
        "history_short": "Тарих",
        "updatedmarker": "соңгы керүемнән соң яңартылган",
-       "printableversion": "Бастыру версиясе",
+       "printableversion": "Басма версиясе",
        "permalink": "Даими сылтама",
        "print": "Бастыру",
        "view": "Карау",
        "prevn": "алдагы {{PLURAL:$1|$1}}",
        "nextn": "чираттагы {{PLURAL:$1|$1}}",
        "prevn-title": "Алдагы $1  {{PLURAL:$1|язма}}",
-       "nextn-title": "Киләсе $1  {{PLURAL:$1|язма}}",
+       "nextn-title": "Киләсе $1 {{PLURAL:$1|язма|язма}}",
        "shown-title": "Сәхифәдә $1 {{PLURAL:$1|1=язма|язма}} күрсәтелсен",
        "viewprevnext": "Күрсәтелүе: ($1 {{int:pipe-separator}} $2) ($3)",
        "searchmenu-exists": "'''Бу вики-проекта «[[:$1]]» исемле бит бар инде'''",
        "searchprofile-images-tooltip": "Файллар эзләү",
        "searchprofile-everything-tooltip": "Барлык битләрдә дә эзләү",
        "searchprofile-advanced-tooltip": "Бирелгән исемнәр мәйданында эзләү",
-       "search-result-size": "$1 ({{PLURAL:$2|$2 сүз}})",
+       "search-result-size": "$1 ({{PLURAL:$2|1 сүз|$2 сүз}})",
        "search-result-category-size": "{{PLURAL:$1|1=1 әгъза|$1 әгъза}} ({{PLURAL:$2|1=1 асттөркем|$2 асттөркем}}, {{PLURAL:$3|1=1 файл|$3 файл}})",
        "search-redirect": "(юнәлтү $1)",
        "search-section": "($1 бүлеге)",
        "number_of_watching_users_pageview": "[$1 {{PLURAL:$1|күзәтеп тора кулланучы}}]",
        "rc_categories": "Төркемнәрдә генә тора («|» бүлүче)",
        "rc_categories_any": "Һәрбер",
+       "rc-change-size-new": "Төзәтмәдән соң күләме: $1 {{PLURAL:$1|байт|байт|байт}}",
        "newsectionsummary": "/* $1 */ яңа бүлек",
        "rc-enhanced-expand": "Ваклыкларны күрсәтү",
        "rc-enhanced-hide": "Ваклыкларны яшерү",
        "import-revision-count": "$1 {{PLURAL:$1|юрама|юрама|юрама}}",
        "importnopages": "Импортлау өчен битләр юк.",
        "importlogpage": "Кертү көндәлеге",
-       "import-logentry-interwiki": "«$1» — викиара  импортлау",
        "tooltip-pt-userpage": "Кулланучы битегез",
        "tooltip-pt-mytalk": "Бәхәс битегез",
        "tooltip-pt-preferences": "Көйләнмәләрегез",
        "tooltip-pt-mycontris": "Сезнең кертеменгезне исемлеге",
        "tooltip-pt-login": "Сез хисап язмасы төзи алыр идегез, әмма бу мәҗбүри түгел.",
        "tooltip-pt-logout": "Чыгу",
+       "tooltip-pt-createaccount": "Сезгә аккаунт ясарга һәм системага керергә киңәш итәбез, әмма бу мәҗбүри түгел.",
        "tooltip-ca-talk": "Битнең эчтәлеге турында бәхәс",
        "tooltip-ca-edit": "Сез бу бит үзгәртә аласыз. Зинһар, саклаганчы карап алуны кулланыгыз.",
        "tooltip-ca-addsection": "Яңа бүлек башлау",
        "tooltip-t-emailuser": "Бу кулланучыга хат җибәрү",
        "tooltip-t-upload": "Файлларны йөкләү",
        "tooltip-t-specialpages": "Барлык махсус битләр исемлеге",
-       "tooltip-t-print": "Бу битнең бастыру версиясе",
+       "tooltip-t-print": "Бу битнең басма версиясе",
        "tooltip-t-permalink": "Битнең бу юрамасына даими сылтама",
        "tooltip-ca-nstab-main": "Мәкаләнең эчтәлеге",
        "tooltip-ca-nstab-user": "Кулланучының шәхси бите",
        "file-nohires": "Югары ачыклык белән юрама юк.",
        "svg-long-desc": "SVG файлы, шартлы $1 × $2 нокта, файлның зурлыгы: $3",
        "show-big-image": "Тулы ачыклык",
+       "show-big-image-size": "$1 × $2 пиксель",
        "newimages": "Яңа сүрәтләр җыелмасы",
        "newimages-legend": "Фильтр",
        "ilsubmit": "Эзләү",
        "logentry-newusers-autocreate": "Автоматик рәвештә $1 хисап язмасы төзелде.",
        "rightsnone": "(юк)",
        "revdelete-summary": "үзгәртүләр тасвирламасы",
+       "feedback-adding": "Фикерне сәхифәгә өстәү ...",
+       "feedback-bugnew": "Мин тикшердем. Яңа хата турында хәбәр итү",
        "feedback-bugornote": "Әгәр дә сез техник проблеманы җентекләп тасвирларга әзер икәнсез, зинһар өчен, [$1 хата турында хәбәр итегез].\nБашка очракта сез түбәндәге гади форманы куллана аласыз. Сезнең шәрехләмә \"[$3 $2]\" сәхифәсенә сезнең кулланучы исеме һәм сез кулланган браузер исеме белән бергә өстәләчәк.",
-       "feedback-subject": "Тема:",
-       "feedback-message": "Хәбәр:",
        "feedback-cancel": "Баш тарту",
-       "feedback-submit": "Фикер җибәрү",
-       "feedback-adding": "Фикерне сәхифәгә өстәү ...",
+       "feedback-close": "Әзер",
        "feedback-error1": "Хата. APIдан билгесез нәтиҗә",
        "feedback-error2": "Хата: төзәтү уңышсыз килеп чыкты",
        "feedback-error3": "Хата: APIдан җавап юк.",
+       "feedback-message": "Хәбәр:",
+       "feedback-subject": "Тема:",
+       "feedback-submit": "Фикер җибәрү",
        "feedback-thanks": "Рәхмәт! Сезнең фикер \"[$2 $1]\" сәхифәсенә куелды.",
-       "feedback-close": "Әзер",
-       "feedback-bugnew": "Мин тикшердем. Яңа хата турында хәбәр итү",
        "searchsuggest-search": "Эзләү",
        "searchsuggest-containing": "эчтәлек...",
        "api-error-badaccess-groups": "Сезгә бу викигә файллар өстәү рөхсәт ителмәгән",
index 0fd95aa..9d2cec7 100644 (file)
        "logentry-block-reblock": "$1将{{GENDER:$4|$3}}的封禁设置{{GENDER:$2|更改为}}持续时间$5 $6",
        "logentry-suppress-block": "$1{{GENDER:$2|封禁了}}{{GENDER:$4|$3}},持续时间$5 $6",
        "logentry-suppress-reblock": "$1将{{GENDER:$4|$3}}的封禁设置{{GENDER:$2|更改为}}持续时间$5 $6",
+       "logentry-import-upload": "$1通过上传{{GENDER:$2|导入}}了$3",
+       "logentry-import-interwiki": "$1从其他wiki{{GENDER:$2|导入}}了$3",
        "logentry-merge-merge": "$1将$3{{GENDER:$2|合并}}至$4(修订版本至$5)",
        "logentry-move-move": "$1{{GENDER:$2|移动}}页面$3至$4",
        "logentry-move-move-noredirect": "$1{{GENDER:$2|移动}}页面$3至$4,不留重定向",
index 464226a..d413434 100644 (file)
                        "Cathypilot0117",
                        "NigelSoft",
                        "EagerLin",
-                       "Cbliu"
+                       "Cbliu",
+                       "Citizen01"
                ]
        },
        "tog-underline": "底線標示連結:",
-       "tog-hideminor": "隱藏近期變中的小修訂",
+       "tog-hideminor": "隱藏近期變中的小修訂",
        "tog-hidepatrolled": "隱藏近期變更中巡查過的編輯",
        "tog-newpageshidepatrolled": "隱藏新頁面清單中巡查過的頁面",
        "tog-extendwatchlist": "展開監視清單顯示包含最近以外的所有變更",
-       "tog-usenewrc": "依近期變動與監視清單頁面分類顯示變更",
+       "tog-usenewrc": "依近期變更與監視清單的頁面分類顯示變更",
        "tog-numberheadings": "標題自動編號",
        "tog-showtoolbar": "顯示編輯工具列",
        "tog-editondblclick": "開啟滑鼠雙擊編輯頁面",
@@ -91,7 +92,7 @@
        "tog-forceeditsummary": "未填寫編輯摘要時提示我",
        "tog-watchlisthideown": "隱藏監視清單中本人的編輯",
        "tog-watchlisthidebots": "隱藏監視清單中機器人的編輯",
-       "tog-watchlisthideminor": "隱藏監視清單中的細微修訂",
+       "tog-watchlisthideminor": "隱藏監視清單中的修訂",
        "tog-watchlisthideliu": "隱藏監視清單中已登入使用者的編輯",
        "tog-watchlisthideanons": "隱藏監視清單中匿名使用者的編輯",
        "tog-watchlisthidepatrolled": "隱藏監視清單中已巡查的編輯",
        "disclaimers": "免責聲明",
        "disclaimerpage": "Project:General disclaimer",
        "edithelp": "編輯説明",
+       "helppage-top-gethelp": "説明",
        "mainpage": "首頁",
        "mainpage-description": "首頁",
        "policy-url": "Project:Policy",
        "youhavenewmessages": "您有 $1 ($2)。",
        "youhavenewmessagesfromusers": "你有來自{{PLURAL:$3|另一位使用者|$3 位使用者}}的 $1 ($2)。",
        "youhavenewmessagesmanyusers": "你有來自多位使用者的 $1 ($2)。",
-       "newmessageslinkplural": "{{PLURAL:$1|一新訊息|999=新訊息}}",
+       "newmessageslinkplural": "{{PLURAL:$1|一新訊息|999=新訊息}}",
        "newmessagesdifflinkplural": "最近{{PLURAL:$1|變更}}",
        "youhavenewmessagesmulti": "您在 $1 有新訊息",
        "editsection": "編輯",
        "readonly_lag": "資料庫已自動鎖定,正在等候次要資料庫同步資料到主要資料庫",
        "internalerror": "內部錯誤",
        "internalerror_info": "內部錯誤:$1",
+       "internalerror-fatal-exception": "嚴重例外類型 \"$1\"",
        "filecopyerror": "無法複製檔案 \"$1\" 至 \"$2\"。",
        "filerenameerror": "無法重新命名檔案 \"$1\" 為 \"$2\"。",
        "filedeleteerror": "無法刪除檔案 \"$1\"。",
        "cannotdelete": "無法刪除頁面或檔案 \"$1\"。\n它可能已經被其他人刪除。",
        "cannotdelete-title": "無法刪除頁面 \"$1\"",
        "delete-hook-aborted": "刪除已被 Hook 中止。\n且未回應無任何說明。",
-       "no-null-revision": "ç\84¡æ³\95å°\8d \"$1\" é \81é\9d¢å»ºç«\8bæ\96°ç\9a\84空白修訂",
+       "no-null-revision": "ç\84¡æ³\95建ç«\8bé \81é\9d¢ \"$1\" ç\9a\84æ\96°空白修訂",
        "badtitle": "無效的標題",
        "badtitletext": "指定的頁面標題是無效、空白,或未正確連結的跨語言或跨 Wiki 的標題。\n標題中可能包含無法使用在標題的字元。",
        "perfcached": "以下為快取資料,可能不是最新的。 快取資料最多可儲存 {{PLURAL:$1|1 筆結果|$1 筆結果}}。",
        "invalidtitle-knownnamespace": "命名空間 \"$2\" 與名稱 \"$3\" 是無效的標題",
        "invalidtitle-unknownnamespace": "不明的命名空間編號 $1 與名稱 \"$2\" 是無效的標題",
        "exception-nologin": "未登入",
-       "exception-nologin-text": "請先登入才能存取頁面或操作。",
+       "exception-nologin-text": "您需要先登入方可存取或者操作此頁面。",
        "exception-nologin-text-manual": "請先 $1 以存取此頁面或操作。",
        "virus-badscanner": "錯誤的設定:不明的病毒掃瞄程式:<em>$1</em>",
        "virus-scanfailed": "掃瞄失敗 (代碼 $1)",
        "userlogin-helplink2": "登入協助",
        "userlogin-loggedin": "您目前已登入 {{GENDER:$1|$1}} 使用者,\n請使用下列表單改登入另一位使用者。",
        "userlogin-createanother": "建立另一個帳號",
-       "createacct-emailrequired": "電子郵地址",
+       "createacct-emailrequired": "電子郵地址",
        "createacct-emailoptional": "電子郵件地址 (選填)",
        "createacct-email-ph": "輸入您的電子郵件地址",
-       "createacct-another-email-ph": "輸入電子郵地址",
-       "createaccountmail": "使用臨時的隨機密碼,並將它寄至指定的電子郵地址",
+       "createacct-another-email-ph": "輸入電子郵地址",
+       "createaccountmail": "使用臨時的隨機密碼,並將它寄至指定的電子郵地址",
        "createacct-realname": "真實姓名 (選填)",
        "createaccountreason": "原因:",
        "createacct-reason": "原因",
        "mailmypassword": "重設密碼",
        "passwordremindertitle": "{{SITENAME}} 的新臨時密碼",
        "passwordremindertext": "不明人士 (可能是您自己,來自 IP 位址 $1) 要求重設在 {{SITENAME}} ($4) 的密碼。\n給使用者 \"$2\" 的臨時密碼設為 \"$3\"。\n如果這個動作是您做的,您需要立即登入並設定一個新的密碼,\n您的臨時密碼將於{{PLURAL:$5|一|$5}}天內過期。\n\n如果不是您要求重設密碼,或您已想起密碼,並不準備修改,\n您可以忽略此訊息並且繼續使用您原本的密碼。",
-       "noemail": "使用者 \"$1\" 沒有電子郵地址記錄。",
-       "noemailcreate": "您需要提供一個有效的電地址。",
-       "passwordsent": "使用者 \"$1\" 的新密碼已寄至當出登記的電子郵件位址,\n請稍後收到信件後再登入。",
+       "noemail": "使用者 \"$1\" 沒有電子郵地址記錄。",
+       "noemailcreate": "您需要提供一個有效的電子郵件地址。",
+       "passwordsent": "使用者 \"$1\" 的新密碼已寄至當出登記的電子郵件地址,\n請稍後收到郵件後再登入。",
        "blocked-mailpassword": "您的 IP 位址已被封鎖不允許編輯,密碼復原的功能也同樣被禁止使用以防止被濫用。",
        "eauthentsent": "已寄出一封確認信到您所設定的電子郵件位址。\n在未收到其它電子郵件前,您必須先依照郵件中的指示,確認這個帳號確實是您本人。",
        "throttled-mailpassword": "密碼重設的電子郵件已經在最近 $1 小時內寄出。\n為防止濫用,$1 小時內只能寄出一次密碼重設信件。",
        "mailerror": "傳送電子郵件錯誤:$1",
        "acct_creation_throttle_hit": "使用您目前的 IP 位址的訪客在最近一天建立了 {{PLURAL:$1|1 個帳號|$1 個帳號}},已超出系統允許的上限。\n因此,目前無法讓使用此 IP 位址的訪客建立帳號。",
-       "emailauthenticated": "您的電子郵地址已於 $2 $3 確認。",
-       "emailnotauthenticated": "您的電地址尚未確認,\n尚不會寄出以下功能的電子郵件給您。",
-       "noemailprefs": "在您的偏好設定中設定電子郵件址,讓您可以使用這些功能。",
-       "emailconfirmlink": "確認您的電地址",
-       "invalidemailaddress": "無法接受格式不正確的電子郵箱地址,請輸入正確的電子郵箱地址格式或略過填寫該欄位。",
-       "cannotchangeemail": "此 Wiki 不允許更改帳號的電子郵件址。",
+       "emailauthenticated": "您的電子郵地址已於 $2 $3 確認。",
+       "emailnotauthenticated": "您的電子郵件地址尚未確認,\n尚不會寄出以下功能的電子郵件給您。",
+       "noemailprefs": "在您的偏好設定中設定電子郵件址,讓您可以使用這些功能。",
+       "emailconfirmlink": "確認您的電子郵件地址",
+       "invalidemailaddress": "無法接受格式不正確的電子郵件地址,請輸入正確的電子郵件地址格式或略過填寫該欄位。",
+       "cannotchangeemail": "此 Wiki 不允許更改帳號的電子郵件址。",
        "emaildisabled": "此網站不能傳送電子郵件。",
        "accountcreated": "已建立帳號",
        "accountcreatedtext": "使用者帳號 [[{{ns:User}}:$1|$1]] ([[{{ns:User talk}}:$1|對話]]) 已建立。",
        "createaccount-title": "{{SITENAME}} 的帳號建立",
-       "createaccount-text": "不明人士使用您的電地址在 {{SITENAME}} ($4) 建立了一個帳號名稱為 \"$2\",密碼為 \"$3\"。\n您應該立即登入並更改密碼。\n\n如果該帳號是建立錯誤的話,您可以忽略此訊息。",
+       "createaccount-text": "不明人士使用您的電子郵件地址在 {{SITENAME}} ($4) 建立了一個帳號名稱為 \"$2\",密碼為 \"$3\"。\n您應該立即登入並更改密碼。\n\n如果該帳號是建立錯誤的話,您可以忽略此訊息。",
        "login-throttled": "您已經嘗試太多次的登入動作。\n請稍等 $1 後再試。",
        "login-abort-generic": "您登入失敗 - 已中止",
        "login-migrated-generic": "您的帳號已轉移,且此 Wiki 中您的使用者名稱已不存在。",
        "passwordreset-domain": "網域名稱:",
        "passwordreset-capture": "檢視電子郵件內容?",
        "passwordreset-capture-help": "若您勾選此核選方塊,電子郵件 (包含臨時密碼) 將直接顯示,並寄給使用者。",
-       "passwordreset-email": "電子郵箱位址:",
+       "passwordreset-email": "電子郵件地址:",
        "passwordreset-emailtitle": "於 {{SITENAME}} 的帳號詳細資訊",
        "passwordreset-emailtext-ip": "不明人士 (可能是您自己,來自 IP 位址 $1) 要求重設在 {{SITENAME}} ($4) 的密碼,下列是與此電子郵件地址有關的使用者{{PLURAL:$3|帳號}}:\n\n$2\n\n{{PLURAL:$3|這個臨時密碼|這些臨時密碼}}將會在{{PLURAL:$5|一天|$5 天}}內到期,\n您應立即登入並更改新的密碼。如果不是您要求重設密碼,或您已想起密碼,並不準備修改,\n您可以忽略本訊息並且繼續使用您原本的密碼。",
        "passwordreset-emailtext-user": "使用者 $1 要求重設在 {{SITENAME}} ($4) 的密碼,下列是與此電子郵件位址有關的使用者{{PLURAL:$3|帳號}}:\n\n$2\n\n{{PLURAL:$3|這個臨時密碼|這些臨時密碼}}將會在{{PLURAL:$5|一天|$5 天}}內到期,\n您應立即登入並更改新的密碼。如果不是您要求重設密碼,或您已想起密碼,並不準備修改,\n您可以忽略此訊息並且繼續使用您原本的密碼。",
        "passwordreset-emailsent": "已寄出重設密碼的電子郵件。",
        "passwordreset-emailsent-capture": "已寄出重設密碼的電子郵件,並於下方顯示。",
        "passwordreset-emailerror-capture": "下列為重設密碼的電子郵件內容,傳送給{{GENDER:$2|使用者}}失敗:$1",
-       "changeemail": "變更電子郵地址",
-       "changeemail-text": "完成此表單以修改您的電子郵地址,您需要輸入您的密碼來確認此次變更。",
+       "changeemail": "變更電子郵地址",
+       "changeemail-text": "完成此表單以修改您的電子郵地址,您需要輸入您的密碼來確認此次變更。",
        "changeemail-no-info": "您必須登入方可直接存取此頁面。",
-       "changeemail-oldemail": "目前的電地址:",
-       "changeemail-newemail": "新的電地址:",
+       "changeemail-oldemail": "目前的電子郵件地址:",
+       "changeemail-newemail": "新的電子郵件地址:",
        "changeemail-none": "(無)",
        "changeemail-password": "您於 {{SITENAME}} 的密碼:",
-       "changeemail-submit": "變更電子郵",
+       "changeemail-submit": "變更電子郵",
        "changeemail-throttled": "您最近嘗試了太多次登入。\n請等待 $1 後再試。",
        "resettokens": "重設金鑰",
        "resettokens-text": "您可以在此重設用來存取您帳號相關隱私資料的金鑰。\n\n若您不小心將您的密鑰分享給其他人或您的帳號已遭到入侵、破壞,應該要重設此金鑰。",
        "subject-preview": "主旨/標題預覽:",
        "blockedtitle": "使用者已被封鎖",
        "blockedtext": "<strong>您的使用者名稱或 IP 位址以被封鎖。</strong>\n\n您被 $1 封鎖,\n原因爲 <em>$2</em>。\n\n* 封鎖開始時間:$8\n* 封鎖結束時間:$6\n* 相關封鎖對象:$7\n\n您可以聯絡 $1 或其他的 [[{{MediaWiki:Grouppage-sysop}}|管理員]] 討論封鎖的相關問題。\n若您已在 [[Special:Preferences|偏好設定]] 中設定了一個有效的電子郵件地址,且尚未被封鎖郵件功能,則您可透過 \"Email 給此使用者\" 的功能來聯絡相關管理員。\n您目前的 IP 位址是 $3,此次封鎖的 ID 為 #$5。\n請您在詢問時附註以上詳細訊息。",
-       "autoblockedtext": "因先前的另一位使用者被 $1 封鎖,您的 IP 位址已被自動封鎖。\n原因是:\n\n:<em>$2</em>\n\n* 封鎖開始時間:$8\n* 封鎖結束時間:$6\n* 相關封鎖對象:$7\n\n您可以聯絡 $1 或其他的 [[{{MediaWiki:Grouppage-sysop}}|管理員]] 討論封鎖的相關問題。\n若您已在 [[Special:Preferences|偏好設定]] 中設定了一個有效的電子郵件地址,且尚未被封鎖郵件功能,則您可透過 「Email 給此使用者」 的功能來聯絡相關管理員。\n您目前的 IP 位址是 $3,此次封鎖的 ID 爲 #$5。\n請您在詢問時附註以上詳細訊息。",
+       "autoblockedtext": "因先前的另一位使用者被 $1 封鎖,您的 IP 位址已被自動封鎖。\n原因是:\n\n:<em>$2</em>\n\n* 封鎖開始時間:$8\n* 封鎖結束時間:$6\n* 相關封鎖對象:$7\n\n您可以聯絡 $1 或其他的 [[{{MediaWiki:Grouppage-sysop}}|管理員]] 討論封鎖的相關問題。\n若您已在 [[Special:Preferences|偏好設定]] 中設定了一個有效的電子郵件地址,且尚未被封鎖郵件功能,則您可透過 \"Email 給此使用者\" 的功能來聯絡相關管理員。\n您目前的 IP 位址是 $3,此次封鎖的 ID 爲 #$5。\n請您在詢問時附註以上詳細訊息。",
        "blockednoreason": "未說明原因",
        "whitelistedittext": "請先 $1 才可編輯頁面。",
-       "confirmedittext": "在編輯此頁之前您必須確認您的電郵地址。\n請透過 [[Special:Preferences|偏好設定]] 設定並驗證您的電郵地址。",
+       "confirmedittext": "在編輯此頁之前您必須確認您的電子郵件地址。\n請透過 [[Special:Preferences|偏好設定]] 設定並驗證您的電子郵件地址。",
        "nosuchsectiontitle": "找不到章節",
        "nosuchsectiontext": "您嘗試編輯的章節並不存在。\n可能在您檢視頁面時已經移動或刪除。",
        "loginreqtitle": "需要登入",
        "last": "前筆",
        "page_first": "第一頁",
        "page_last": "最後頁",
-       "histlegend": "比較選擇的版本差異:選要比較修訂版本的單選方塊並點選底部的按鈕進行比較。<br />\n符號說明:<strong>({{int:cur}})</strong> = 與最新的修訂版本比較,<strong>({{int:last}})</strong> = 與前一筆修訂版本比較,<strong>{{int:minoreditletter}}</strong> = 小修訂。",
+       "histlegend": "比對選擇的版本差異:選擇要比對修訂版本的單選方塊並點選底部的按鈕進行比對。<br />\n符號說明:<strong>({{int:cur}})</strong> = 與最新的修訂版本比對,<strong>({{int:last}})</strong> = 與前一筆修訂版本比對,<strong>{{int:minoreditletter}}</strong> = 小修訂。",
        "history-fieldset-title": "瀏覽歷史",
        "history-show-deleted": "只顯示已刪除的修訂",
        "histfirst": "最舊",
        "logdelete-text": "已刪除的日誌活動仍會出現於日誌中,但其部分內容將不會向公眾開放存取。",
        "revdelete-text-others": "若未設定額外條件,其他管理員仍有權限檢視與取消刪除隱藏的內容。",
        "revdelete-confirm": "請確認您是否明白此動作會造成的後果,以及您所做的動作是否符合[[{{MediaWiki:Policy-url}}|政策]]規範。",
-       "revdelete-suppress-text": "隱藏顯示應<strong>只有</strong>在下述情形時使用:\n* 潛在誹謗的資訊\n* 不恰當的個人資料\n*: <em>住家地址、電話號碼、身分證號碼等。</em>",
+       "revdelete-suppress-text": "禁止顯示應<strong>只有</strong>在下述情形時使用:\n* 潛在誹謗的資訊\n* 不恰當的個人資料\n*: <em>住家地址、電話號碼、身分證號碼等。</em>",
        "revdelete-legend": "設定顯示限制",
        "revdelete-hide-text": "修訂文字",
        "revdelete-hide-image": "隱藏檔案內容",
        "prefs-labs": "實驗中的功能",
        "prefs-user-pages": "使用者頁面",
        "prefs-personal": "使用者基本資料",
-       "prefs-rc": "近期變",
+       "prefs-rc": "近期變",
        "prefs-watchlist": "監視清單",
        "prefs-editwatchlist": "編輯監視清單",
        "prefs-editwatchlist-label": "編輯在您監視清單上的項目:",
        "prefs-watchlist-token": "監視清單金鑰:",
        "prefs-misc": "其他",
        "prefs-resetpass": "變更密碼",
-       "prefs-changeemail": "變更電子郵地址",
-       "prefs-setemail": "設定電子郵地址",
-       "prefs-email": "電子郵選項",
+       "prefs-changeemail": "變更電子郵地址",
+       "prefs-setemail": "設定電子郵地址",
+       "prefs-email": "電子郵選項",
        "prefs-rendering": "外觀",
        "saveprefs": "儲存",
        "restoreprefs": "還原所有預設設定 (所有項目)",
        "searchresultshead": "搜尋",
        "stub-threshold": "<a href=\"#\" class=\"stub\">短頁面連結</a>格式門檻值 (位元組):",
        "stub-threshold-disabled": "已停用",
-       "recentchangesdays": "近期變顯示的天數:",
+       "recentchangesdays": "近期變顯示的天數:",
        "recentchangesdays-max": "最多 $1 {{PLURAL:$1|天}}",
        "recentchangescount": "預設顯示的編輯數:",
-       "prefs-help-recentchangescount": "這包含近期變、頁面歷史以及日誌。",
+       "prefs-help-recentchangescount": "這包含近期變、頁面歷史以及日誌。",
        "prefs-help-watchlist-token2": "訂閱您的監視清單所需的金鑰。\n任何人只要知道金鑰就能夠讀取您的監視清單,所以請勿任意與它人共享。\n若有需要 [[Special:ResetTokens|您可重設金鑰]]。",
        "savedprefs": "已儲存您的偏好設定。",
        "timezonelegend": "時區:",
        "prefs-custom-js": "自訂 JavaScript",
        "prefs-common-css-js": "所有外觀共用的 CSS/JavaScript:",
        "prefs-reset-intro": "您可以使用此頁面重設您的偏好設定為網站預設值。\n這個動作將無法復原。",
-       "prefs-emailconfirm-label": "電子郵確認:",
-       "youremail": "電子郵箱:",
+       "prefs-emailconfirm-label": "電子郵確認:",
+       "youremail": "Email:",
        "username": "{{GENDER:$1|使用者名稱}}:",
        "prefs-memberingroups": "{{GENDER:$2|所屬}}{{PLURAL:$1|群組}}:",
        "prefs-registration": "註冊時間:",
        "gender-male": "他編輯了 Wiki 頁面",
        "gender-female": "她編輯了 Wiki 頁面",
        "prefs-help-gender": "此偏好設定為選填欄位。\n系統會使用您選擇的方式稱呼您,對他人提及您時也會使用適當語法稱呼。\n此項資訊會被公開。",
-       "email": "電子郵箱",
+       "email": "Email",
        "prefs-help-realname": "真實姓名為選填欄位。\n若提供,真實姓名可能會用來作為您的作品的署名。",
        "prefs-help-email": "電子郵件地址為選填欄位。\n但在重設密碼時會使用,而您很有可能會忘記密碼。",
-       "prefs-help-email-others": "您亦可以選擇讓其他使用者透過您的電子郵箱、使用者頁面或對話頁面的連結與您聯絡。\n您的電子郵箱地址不會洩漏給其他要聯絡您的使用者。",
+       "prefs-help-email-others": "您亦可以選擇讓其他使用者透過您的電子郵件、使用者頁面或對話頁面的連結與您聯絡。\n您的電子郵件地址不會洩漏給其他要聯絡您的使用者。",
        "prefs-help-email-required": "電子郵件地址是必填項目。",
        "prefs-info": "基本資訊",
        "prefs-i18n": "國際化",
        "prefs-help-prefershttps": "此偏好設定將於您下次登入時生效。",
        "prefswarning-warning": "您對您的偏好設定所做的變更尚未儲存。\n若您未點選 \"$1\" 離開此頁面,將不會更新您的偏好設定。",
        "prefs-tabs-navigation-hint": "提示:您可使用左、右方向鍵切換頁籤。",
-       "email-address-validity-valid": "電子郵地址有效",
-       "email-address-validity-invalid": "請輸入一個有效的電子郵件址",
+       "email-address-validity-valid": "電子郵地址有效",
+       "email-address-validity-invalid": "請輸入一個有效的電子郵件址",
        "userrights": "使用者權限管理",
        "userrights-lookup-user": "管理使用者群組",
        "userrights-user-editname": "請輸入使用者名稱:",
        "group-bot-member": "機器人",
        "group-sysop-member": "{{GENDER:$1|管理員}}",
        "group-bureaucrat-member": "行政員",
-       "group-suppress-member": "監督員",
+       "group-suppress-member": "{{GENDER:$1|監督員}}",
        "grouppage-user": "{{ns:project}}:使用者",
        "grouppage-autoconfirmed": "{{ns:project}}:自動確認使用者",
        "grouppage-bot": "{{ns:project}}:機器人",
        "grouppage-sysop": "{{ns:project}}:管理員",
        "grouppage-bureaucrat": "{{ns:project}}:行政員",
-       "grouppage-suppress": "{{ns:project}}:監督",
+       "grouppage-suppress": "{{ns:project}}:監督",
        "right-read": "閱讀頁面",
        "right-edit": "編輯頁面",
        "right-createpage": "建立頁面 (不含討論頁面)",
        "right-editmyuserjs": "編輯自己的使用者 JavaScript 檔",
        "right-viewmywatchlist": "檢視自己的監視清單",
        "right-editmywatchlist": "編輯自己的監視清單。注意,即使無此權限,某些操作仍會新增頁面至監視清單。",
-       "right-viewmyprivateinfo": "檢視自己的私隱資料(如:電子郵件地址及真實姓名)",
-       "right-editmyprivateinfo": "編輯自己的隱私資料(如:電子郵件地址及真實姓名)",
+       "right-viewmyprivateinfo": "檢視自己的私隱資料 (如:電子郵件地址及真實姓名)",
+       "right-editmyprivateinfo": "編輯自己的隱私資料 (如:電子郵件地址及真實姓名)",
        "right-editmyoptions": "編輯自己的偏好設定",
        "right-rollback": "快速還原最後一位使用者對某一頁面的編輯",
        "right-markbotedits": "標示還原編輯為機械人編輯",
        "action-editcontentmodel": "編輯頁面的內容模型",
        "action-managechangetags": "建立並自資料庫移除標籤",
        "nchanges": "$1 次變更",
-       "enhancedrc-since-last-visit": "{{PLURAL:$1|自上次訪}}已有 $1",
+       "enhancedrc-since-last-visit": "{{PLURAL:$1|自上次訪}}已有 $1",
        "enhancedrc-history": "歷史",
-       "recentchanges": "近期變",
-       "recentchanges-legend": "近期變選項",
-       "recentchanges-summary": "追蹤 Wiki 中此頁面的近期變。",
+       "recentchanges": "近期變",
+       "recentchanges-legend": "近期變選項",
+       "recentchanges-summary": "追蹤 Wiki 中此頁面的近期變。",
        "recentchanges-noresult": "於指定時間內沒有符合條件的變更。",
-       "recentchanges-feed-description": "追蹤 Wiki 中此訂閱來源的近期變。",
+       "recentchanges-feed-description": "追蹤 Wiki 中此訂閱來源的近期變。",
        "recentchanges-label-newpage": "該編輯建立了新頁面",
        "recentchanges-label-minor": "該編輯是一個小修訂",
        "recentchanges-label-bot": "該編輯由機器人執行",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (請參考[[Special:NewPages|新頁面]])",
        "recentchanges-legend-plusminus": "(<em>±123</em>)",
        "rcnotefrom": "以下{{PLURAL:$5|為}}自 <strong>$3 $4</strong> 以來的變更 (最多顯示 <strong>$1</strong> 筆)。",
-       "rclistfrom": "顯示自 $3 $2 以來的近期變",
+       "rclistfrom": "顯示自 $3 $2 以來的近期變",
        "rcshowhideminor": "$1 小修訂",
        "rcshowhideminor-show": "顯示",
        "rcshowhideminor-hide": "隱藏",
        "rcshowhideliu": "$1 已註冊的使用者",
        "rcshowhideliu-show": "顯示",
        "rcshowhideliu-hide": "隱藏",
-       "rcshowhideanons": "$1 匿名使用者",
+       "rcshowhideanons": "$1 匿名使用者",
        "rcshowhideanons-show": "顯示",
        "rcshowhideanons-hide": "隱藏",
        "rcshowhidepatr": "$1 巡查過的編輯",
        "rcshowhidemine": "$1 我的編輯",
        "rcshowhidemine-show": "顯示",
        "rcshowhidemine-hide": "隱藏",
-       "rclinks": "顯示近期 $2 天內的 $1 次變。<br />$3",
+       "rclinks": "顯示近期 $2 天內的 $1 次變。<br />$3",
        "diff": "差異",
        "hist": "歷史",
        "hide": "隱藏",
        "unusedimages": "未使用的檔案",
        "wantedcategories": "需要的分類",
        "wantedpages": "需要的頁面",
+       "wantedpages-summary": "以下為最多連結的不存在頁面,除只有重新導向連結的頁面外。 若要取得不存在的重新導向頁面,請至 [[{{#special:BrokenRedirects}}]]。",
        "wantedpages-badtitle": "結果集合中的標題無效:$1",
        "wantedfiles": "需要的檔案",
        "wantedfiletext-cat": "下列檔案被時用,但檔案不存在。 外部儲存庫的檔案儘管存在,但此清單仍會列出。 這類誤報的項目會以 <del>刪除線</del> 標示。 另外,頁面內嵌檔案不存在會於清單 [[:$1]] 中顯示。",
        "mostinterwikis": "最多跨 Wiki 的頁面",
        "mostrevisions": "最多修訂的頁面",
        "prefixindex": "所有頁面與字首",
-       "prefixindex-namespace": "所有含字首的頁面 ($1 命名空間)",
+       "prefixindex-namespace": "所有含字首的頁面 ($1 命名空間)",
        "prefixindex-strip": "於清單中省略字首",
        "shortpages": "過短的頁面",
        "longpages": "過長的頁面",
        "nopagetext": "您所指定的目標頁面並不存在。",
        "pager-newer-n": "較新 $1 筆",
        "pager-older-n": "較舊 $1 筆",
-       "suppress": "監督",
+       "suppress": "失職",
        "querypage-disabled": "此特殊頁面因考量效能問題已被停用。",
        "apihelp": "API 說明",
        "apihelp-no-such-module": "查無模組 \"$1\"。",
        "booksources-text": "下列清單包含其他銷售新書籍或二手書籍的網站連結,可會有你想尋找書籍的進一部資訊:",
        "booksources-invalid-isbn": "您提供的 ISBN 不正確,請檢查複製的來源是否有誤。",
        "specialloguserlabel": "執行者:",
-       "speciallogtitlelabel": "目標(標題或使用者):",
+       "speciallogtitlelabel": "目標 (標題或使用者):",
        "log": "日誌",
        "all-logs-page": "所有公開日誌",
        "alllogstext": "合併顯示所有 {{SITENAME}} 中所有類型的日誌。\n您可以點選下拉式選單選擇日誌的類型,指定使用者名稱 (區分大小寫) 或影響的頁面 (區分大小寫)。",
        "allpagesfrom": "顯示頁面開始於:",
        "allpagesto": "顯示頁面結束於:",
        "allarticles": "所有頁面",
-       "allinnamespace": "所有頁面 ($1 命名空間)",
+       "allinnamespace": "所有頁面 ($1 命名空間)",
        "allpagessubmit": "執行",
        "allpagesprefix": "顯示以此為字首頁面:",
        "allpagesbadtitle": "指定的頁面標題無效、包含內部語言或內部 Wiki 的字首。\n它可能包含一個或多個的不能用於標題的字元。",
        "emailpagetext": "您可以使用以下表格傳送電子郵件給這位 {{Gender:$1|使用者}}。\n您在 [[Special:Preferences|偏好設定]] 中所輸入的電子郵件位址將會作為郵件的 \"寄件人\",因此該使用者可直接回覆您。",
        "defemailsubject": "{{SITENAME}} 使用者 \"$1\" 寄來的電子郵件",
        "usermaildisabled": "使用者電子郵件已停用",
-       "usermaildisabledtext": "您不能傳送郵件到本 Wiki 上的其他使用者",
-       "noemailtitle": "沒有電子郵件位址",
+       "usermaildisabledtext": "æ\82¨ä¸\8dè\83½å\82³é\80\81é\9b»å­\90é\83µä»¶å\88°æ\9c¬ Wiki ä¸\8aç\9a\84å\85¶ä»\96使ç\94¨è\80\85",
+       "noemailtitle": "無電子郵件地址",
        "noemailtext": "此使用者尚未指定一個有效的電子郵件地址。",
        "nowikiemailtext": "此使用者選擇不接收其他使用者的信件。",
        "emailnotarget": "收件人不存在或無效的使用者名稱。",
        "emailsent": "已寄出電子郵件",
        "emailsenttext": "已寄出您的電子郵件訊息。",
        "emailuserfooter": "這封電子郵件是由 $1 透過 {{SITENAME}} 的 \"Email 給此使用者\" 功能寄給 $2。",
-       "usermessage-summary": "留下系統訊息。",
+       "usermessage-summary": "留訊息至系統。",
        "usermessage-editor": "系統訊息",
        "watchlist": "監視清單",
        "mywatchlist": "監視清單",
        "thumbnail_image-missing": "檔案遺失:$1",
        "thumbnail_image-failure-limit": "最近顯示此縮圖已發生太多次失敗 ($1 次或更多),請稍後再試。",
        "import": "匯入頁面",
-       "importinterwiki": "Transwiki 匯入",
-       "import-interwiki-text": "請選擇一個 Wiki 與頁面標題以進行匯入。\n會同時記錄修訂日期和編輯者的名稱。\n所有的 Transwiki 匯入操作會被記錄在 [[Special:Log/import|匯入日誌]]。",
+       "importinterwiki": "從其他 wiki 匯入",
+       "import-interwiki-text": "請選擇一個 Wiki 與頁面標題以進行匯入。\n會同時記錄修訂日期和編輯者的名稱。\n所有的從其他 Wiki 匯入操作都會被記錄在 [[Special:Log/import|匯入日誌]]。",
        "import-interwiki-sourcewiki": "來源 Wiki:",
        "import-interwiki-sourcepage": "來源頁面:",
        "import-interwiki-history": "複製此頁的所有歷史修訂",
        "importcantopen": "無法開啟匯入檔案",
        "importbadinterwiki": "無效的 Interwiki 連結",
        "importsuccess": "已完成匯入!",
-       "importnosources": "未定義任何 Transwiki 匯入來源且已關閉使用歷史記錄上傳功能。",
+       "importnosources": "未定義任何已匯入的 wiki 來源且已關閉使用歷史記錄上傳功能。",
        "importnofile": "未上傳匯入檔案。",
        "importuploaderrorsize": "上傳匯入檔案失敗。\n檔案大小超過允許上傳大小。",
        "importuploaderrorpartial": "上傳匯入檔案失敗。\n僅上傳部份檔案。",
        "tooltip-n-mainpage-description": "前往首頁",
        "tooltip-n-portal": "關於本專案、您可以做什麼、哪裡可以找到您需要的事物",
        "tooltip-n-currentevents": "於最新動態中尋找背景資訊",
-       "tooltip-n-recentchanges": "列出此 Wiki 中的近期變清單",
+       "tooltip-n-recentchanges": "列出此 Wiki 中的近期變清單",
        "tooltip-n-randompage": "隨機進入一個頁面",
        "tooltip-n-help": "尋求協助的地方",
        "tooltip-t-whatlinkshere": "列出所有連結此頁面的頁面",
        "pageinfo-few-watchers": "少於 $1 名監視者",
        "pageinfo-redirects-name": "指向此頁面的重新導向頁面數量",
        "pageinfo-subpages-name": "此頁面的子頁面數",
-       "pageinfo-subpages-value": "$1 ($2 個{{PLURAL:$2|重新導向}}; $3 個{{PLURAL:$3|非重新導向}})",
+       "pageinfo-subpages-value": "$1 ($2 個{{PLURAL:$2|重新導向}}; $3 個{{PLURAL:$3|非重新導向}})",
        "pageinfo-firstuser": "頁面建立者",
        "pageinfo-firsttime": "頁面建立日期",
        "pageinfo-lastuser": "最近編輯者",
        "exif-countrydest": "顯示國家",
        "exif-countrycodedest": "顯示國家代碼",
        "exif-provinceorstatedest": "顯示省或州",
-       "exif-citydest": "顯示城市",
+       "exif-citydest": "顯示城市",
        "exif-sublocationdest": "顯示城市詳細地點",
        "exif-objectname": "簡稱",
        "exif-specialinstructions": "特別說明",
        "exif-editstatus": "圖片編輯狀態",
        "exif-urgency": "緊急性",
        "exif-fixtureidentifier": "夾具名稱",
-       "exif-locationdest": "位置描述",
+       "exif-locationdest": "描繪地點",
        "exif-locationdestcode": "位置代碼描述",
-       "exif-objectcycle": "åª\92é«\94ç\9a\84時間",
+       "exif-objectcycle": "åª\92é«\94ç\99¼ç\94\9f時間",
        "exif-contact": "聯絡資訊",
        "exif-writer": "作家",
        "exif-languagecode": "語言",
        "exif-intellectualgenre": "項目類型",
        "exif-subjectnewscode": "主題代碼",
        "exif-scenecode": "IPTC 現場代碼",
-       "exif-event": "事件描述",
-       "exif-organisationinimage": "組織描述",
-       "exif-personinimage": "æ\89\80æ\8f\8fè¿°ç\9a\84人",
+       "exif-event": "描繪事件",
+       "exif-organisationinimage": "描繪組織",
+       "exif-personinimage": "æ\8f\8f繪人ç\89©",
        "exif-originalimageheight": "裁切前的高度",
        "exif-originalimagewidth": "裁切前的寬度",
        "exif-compression-1": "未壓縮",
        "exif-sensingmethod-8": "連續彩色線性感測器",
        "exif-filesource-3": "數位相機",
        "exif-scenetype-1": "直接照像圖片",
-       "exif-customrendered-0": "標準處理",
-       "exif-customrendered-1": "自定義處理",
+       "exif-customrendered-0": "一般程序",
+       "exif-customrendered-1": "自訂程序",
        "exif-exposuremode-0": "自動曝光",
        "exif-exposuremode-1": "手動曝光",
-       "exif-exposuremode-2": "自動曝光感知調節",
+       "exif-exposuremode-2": "自動包圍曝光",
        "exif-whitebalance-0": "自動白平衡",
        "exif-whitebalance-1": "手動白平衡",
        "exif-scenecapturetype-0": "標準",
        "exif-gpslongitude-w": "西經",
        "exif-gpsaltitude-above-sealevel": "海拔 $1 {{PLURAL:$1|公尺}}",
        "exif-gpsaltitude-below-sealevel": "海拔負 $1 {{PLURAL:$1|公尺}}",
-       "exif-gpsstatus-a": "測é\87\8fé\81\8eç¨\8b",
-       "exif-gpsstatus-v": "互動測量",
+       "exif-gpsstatus-a": "測é\87\8fé\80²è¡\8c中",
+       "exif-gpsstatus-v": "測量互通性",
        "exif-gpsmeasuremode-2": "二維測量",
        "exif-gpsmeasuremode-3": "三維測量",
        "exif-gpsspeed-k": "每小時公里",
        "exif-ycbcrpositioning-1": "中間",
        "exif-ycbcrpositioning-2": "同時取樣",
        "exif-dc-contributor": "貢獻者",
-       "exif-dc-coverage": "時間或空間性介質範圍",
+       "exif-dc-coverage": "媒體的時空範圍",
        "exif-dc-date": "日期",
        "exif-dc-publisher": "出版商",
        "exif-dc-relation": "相關媒體",
        "exif-dc-rights": "權利",
        "exif-dc-source": "來源媒體",
-       "exif-dc-type": "介質類型",
+       "exif-dc-type": "媒體類型",
        "exif-rating-rejected": "已拒絕",
        "exif-isospeedratings-overflow": "大於 65535",
        "exif-iimcategory-ace": "藝術、文化與娛樂",
        "exif-urgency-other": "使用者自訂優先權 ($1)",
        "namespacesall": "全部",
        "monthsall": "全部",
-       "confirmemail": "確認電子郵地址",
-       "confirmemail_noemail": "您尚未在 [[Special:Preferences|偏好設定]] 中輸入一個有效的電子郵地址。",
-       "confirmemail_text": "{{SITENAME}}要求您在使用郵件功能之前驗證您的電子郵箱地址。\n點選以下按鈕可向您的電子郵箱傳送一封確認郵件。該郵件包含有一行代碼連結;\n請在您的瀏覽器中載入此連結以確認您的電子郵箱地址是有效的。",
+       "confirmemail": "確認電子郵地址",
+       "confirmemail_noemail": "您尚未在 [[Special:Preferences|偏好設定]] 中輸入一個有效的電子郵地址。",
+       "confirmemail_text": "{{SITENAME}} 要求您在使用郵件功能之前驗證您的電子郵件地址。\n點選以下按鈕可向您的電子郵件傳送一封確認郵件。該郵件包含有一行代碼連結;\n請在您的瀏覽器中載入此連結以確認您的電子郵件地址是有效的。",
        "confirmemail_pending": "確認碼已傳送至您的電子郵件,\n若您才剛建立好您的帳號,可能需要稍後幾分鐘才能收到。\n若沒有收到,請再重新申請一次確認碼。",
-       "confirmemail_send": "電子郵箱確認代碼",
+       "confirmemail_send": "電子郵件確認碼",
        "confirmemail_sent": "確認郵件已寄出。",
-       "confirmemail_oncreate": "確認碼已傳送至您的電子郵件地址。\n登入動作不需要使用此代碼,但開啟在 Wiki 中任何以電子郵件為基礎的功能會需要先提供此代碼。",
-       "confirmemail_sendfailed": "{{SITENAME}}無法傳送確認郵件,請檢查電子郵件址是否包含非法字元。\n\n郵件傳送員回應: $1",
+       "confirmemail_oncreate": "確認碼已傳送至您的電子郵件地址。\n登入動作不需要使用此代碼,但開啟在 Wiki 中任何以電子郵件為基礎的功能會需要先提供此代碼。",
+       "confirmemail_sendfailed": "{{SITENAME}}無法傳送確認郵件,請檢查電子郵件址是否包含非法字元。\n\n郵件傳送員回應: $1",
        "confirmemail_invalid": "無效的確認碼,該代碼可能已經過期。",
-       "confirmemail_needlogin": "請 $1 以確認您的電子郵地址。",
-       "confirmemail_success": "您的電子郵已經被確認。您現在可以 [[Special:UserLogin|登入]] 並使用此網站了。",
-       "confirmemail_loggedin": "已確認您的電子郵地址。",
-       "confirmemail_subject": "{{SITENAME}} 電子郵地址確認",
+       "confirmemail_needlogin": "請 $1 以確認您的電子郵地址。",
+       "confirmemail_success": "您的電子郵已經被確認。您現在可以 [[Special:UserLogin|登入]] 並使用此網站了。",
+       "confirmemail_loggedin": "已確認您的電子郵地址。",
+       "confirmemail_subject": "{{SITENAME}} 電子郵地址確認",
        "confirmemail_body": "不明人士 (可能是您自己,來自 IP 位址 $1) 已在 {{SITENAME}} 註冊了一個帳號 「$2」 並使用了此 Email 地址。\n\n請確認這個帳號是屬於您的,並使用瀏覽器開啟下方連結以開啟在 {{SITENAME}} 上的電子郵件功能:\n\n$3\n\n若您 *未* 註冊此帳號,\n請開啟下方連結取消電子郵件確認:\n\n$5\n\n此確認代碼會於 $4 過期。",
-       "confirmemail_body_changed": "不明人士 (可能是您自己,來自 IP 位址 $1)  已將在 {{SITENAME}} 帳號 \"$2\" 的電子郵件址更改至此。\n\n請確認這個帳號是屬於您的,並使用瀏覽器開啟下方連結以開啟在 {{SITENAME}} 上的電子郵件功能:\n\n$3\n\n若您 *未* 註冊此帳號,\n請開啟下方連結取消電子郵件確認:\n\n$5\n\n此確認代碼會於 $4 過期。",
+       "confirmemail_body_changed": "不明人士 (可能是您自己,來自 IP 位址 $1)  已將在 {{SITENAME}} 帳號 \"$2\" 的電子郵件址更改至此。\n\n請確認這個帳號是屬於您的,並使用瀏覽器開啟下方連結以開啟在 {{SITENAME}} 上的電子郵件功能:\n\n$3\n\n若您 *未* 註冊此帳號,\n請開啟下方連結取消電子郵件確認:\n\n$5\n\n此確認代碼會於 $4 過期。",
        "confirmemail_body_set": "不明人士 (可能是您自己,來自 IP 位址 $1) 已將在 {{SITENAME}} 帳號 「$2」 的電子郵件地址設定至此。\n\n請確認這個帳號是屬於您的,並使用瀏覽器開啟下方連結以開啟在 {{SITENAME}} 上的電子郵件功能:\n\n$3\n\n若您 *未* 註冊此帳號,\n請開啟下方連結取消電子郵件確認:\n\n$5\n\n此確認代碼會於 $4 過期。",
-       "confirmemail_invalidated": "已取消電子郵地址確認",
+       "confirmemail_invalidated": "已取消電子郵地址確認",
        "invalidateemail": "取消電子郵件確認",
        "scarytranscludedisabled": "[Interwiki 轉換代碼不可用]",
        "scarytranscludefailed": "[模板 $1 讀取失敗]",
        "tags-create-submit": "建立",
        "tags-create-no-name": "您必須指定一個標籤名稱。",
        "tags-create-invalid-chars": "標籤名稱不可包含逗號 (<code>,</code>) 或斜線 (<code>/</code>)。",
+       "tags-create-invalid-title-chars": "標籤名稱不能含有無法使用者頁面標題的字元。",
        "tags-create-already-exists": "標籤 \"$1\" 已存在。",
+       "tags-create-warnings-above": "嘗試建立標籤 \"$1\" 時發生下列{{PLURAL:$2|警告}}:",
        "tags-create-warnings-below": "您是否要繼續建立標籤?",
        "tags-delete-title": "刪除標籤",
        "tags-delete-explanation-initial": "您正要從資料庫刪除標籤 \"$1\"。",
+       "tags-delete-explanation-in-use": "標籤會自目前正在使用的{{PLURAL:$2| $2 個修訂或日誌項目| $2 修訂或日誌項目}}中移除。",
+       "tags-delete-explanation-warning": "此動作是 <strong>無法還原的</strong> 且 <strong>無法取消的</strong>,即使是資料庫管理者也無法。 請確認您要刪除的標題。",
+       "tags-delete-explanation-active": "<strong>標籤 \"$1\" 尚在使用,無法刪除。</strong> 要停止標籤使用,請至套用該標籤的頁面,並於該處將標籤停用。",
        "tags-delete-reason": "原因:",
        "tags-delete-submit": "無法取消刪除此標籤",
+       "tags-delete-not-allowed": "無法刪除由擴充套件定義的標籤,除非該擴充套件允許。",
        "tags-delete-not-found": "標籤 \"$1\" 不存在。",
+       "tags-delete-too-many-uses": "標籤 \"$1\" 會套用至 $2 筆以上的{{PLURAL:$2|修訂|修訂}},這代表該標籤將無法刪除。",
+       "tags-delete-warnings-after-delete": "標籤 \"$1\" 已刪除成功,但發生下列{{PLURAL:$2|警告|警告}}:",
        "tags-activate-title": "啟動標籤",
        "tags-activate-question": "您正要啟動標籤 \"$1\"。",
        "tags-activate-reason": "原因:",
        "compare-revision-not-exists": "您所指定的修訂不存在。",
        "dberr-problems": "抱歉!這個網站出現了一些技術上的問題。",
        "dberr-again": "嘗試等候數分鐘後,然後再試。",
-       "dberr-info": "(無法存取資料庫: $1)",
-       "dberr-info-hidden": "(無法連接資料庫)",
+       "dberr-info": "(無法存取資料庫:$1)",
+       "dberr-info-hidden": "(無法存取資料庫)",
        "dberr-usegoogle": "您可以嘗試在此期間使用 Google 搜尋。",
        "dberr-outofdate": "注意,它們用來建立索引的內容可能不是最新的。",
        "dberr-cachederror": "這是請求面頁面的快取複本,可能不是最新的。",
        "revdelete-uname-unhid": "取消隱藏使用者名稱",
        "revdelete-restricted": "已套用對管理員的限制",
        "revdelete-unrestricted": "已移除對管理員的限制",
+       "logentry-block-block": "$1 {{GENDER:$2|已封鎖}} {{GENDER:$4|$3}} 期限為 $5 $6",
+       "logentry-block-unblock": "$1 {{GENDER:$2|已解除封鎖}} {{GENDER:$4|$3}}",
+       "logentry-block-reblock": "$1 {{GENDER:$2|已變更}} {{GENDER:$4|$3}} 的封鎖設定期限為 $5 $6",
+       "logentry-suppress-block": "$1 {{GENDER:$2|已封鎖}} {{GENDER:$4|$3}} 期限為 $5 $6",
+       "logentry-suppress-reblock": "$1 {{GENDER:$2|已變更}} {{GENDER:$4|$3}} 的封鎖設定期限為 $5 $6",
+       "logentry-import-upload": "$1 已由檔案上傳{{GENDER:$2|匯入}} $3",
+       "logentry-import-interwiki": "$1 已由其他 wiki {{GENDER:$2|匯入}} $3",
        "logentry-merge-merge": "$1 將 $3 {{GENDER:$2|合併}}至 $4 (修訂版本至 $5)",
        "logentry-move-move": "$1 {{GENDER:$2|已移動}}頁面 $3 至 $4",
        "logentry-move-move-noredirect": "$1 {{GENDER:$2|已移動}}頁面 $3 至 $4,不保留重新導向",
        "logentry-upload-revert": "$1 {{GENDER:$2|已上傳}} $3",
        "log-name-managetags": "標籤管理日誌",
        "log-description-managetags": "此頁面列出與[[Special:Tags|標籤]]相關的管理工作項目。 在日誌中僅包含由管理員手動所做的操作;被 Wiki 軟體所建立或刪除的標籤項目,不會記錄在此日誌中",
+       "logentry-managetags-create": "$1 {{GENDER:$2|已建立}}標籤 \"$4\"",
+       "logentry-managetags-delete": "$1 {{GENDER:$2|已刪除}}標籤 \"$4\" (已自 $5 個{{PLURAL:$5|修訂或日誌|修訂或日誌}}中移除)",
+       "logentry-managetags-activate": "$1 {{GENDER:$2|已啟用}}標籤 \"$4\" 供使用者與機器人使用",
+       "logentry-managetags-deactivate": "$1 {{GENDER:$2|已停用}}標籤 \"$4\" 供使用者與機器人使用",
        "rightsnone": "(無)",
        "revdelete-summary": "編輯摘要",
        "feedback-adding": "正在新增意見回饋至頁面...",
+       "feedback-back": "返回",
        "feedback-bugcheck": "很好! 檢查一下您的意見是不是已經被列為 [$1 已知問題]。",
        "feedback-bugnew": "我已經檢查。 回報新問題",
        "feedback-bugornote": "如果您準備要詳細描述一個技術問題,請至 [$1 回報問題]。\n或您可以使用以下的簡易表單回報問題,您的使用者名稱與評論將被新增到 [$3 $2] 頁面。",
        "feedback-cancel": "取消",
        "feedback-close": "完成",
+       "feedback-external-bug-report-button": "回報技術問題",
+       "feedback-dialog-title": "送出意見回饋",
+       "feedback-dialog-intro": "您可以使用以下簡易表單傳送您的意見回饋。您的意見將會使用您的使用者名稱新增至頁面 \"$1\"。",
+       "feedback-error-title": "錯誤",
        "feedback-error1": "錯誤:無法識別 API 回傳的結果",
        "feedback-error2": "錯誤:編輯失敗",
        "feedback-error3": "錯誤:API 沒有回應",
        "feedback-message": "訊息:",
        "feedback-subject": "主旨:",
        "feedback-submit": "送出",
+       "feedback-terms": "我了解我的使用者代理資訊包含完整的瀏覽器與作業系統版本資訊,且該資訊將會與意見回饋一同公開共享。",
+       "feedback-termsofuse": "我同意依照使用條款提供意見回饋。",
        "feedback-thanks": "感謝!您的意見回饋已發佈到頁面 \"[$2 $1]\"。",
+       "feedback-thanks-title": "感謝您!",
+       "feedback-useragent": "使用者代理:",
        "searchsuggest-search": "搜尋",
        "searchsuggest-containing": "包含...",
        "api-error-badaccess-groups": "您沒有權限在此 Wiki 上傳檔案。",
        "json-error-utf8": "格式不正確的 UTF-8 字元,可能被不正確編碼",
        "json-error-recursion": "在資料中有一個或多個的遞迴參照值被編碼",
        "json-error-inf-or-nan": "在資料中有一個或多個的 NAN 或 INF 值被編碼",
-       "json-error-unsupported-type": "下列的資料型態無法被編碼"
+       "json-error-unsupported-type": "下列的資料型態無法被編碼",
+       "headline-anchor-title": "連結至此章節"
 }
index e0848d0..c9994e2 100644 (file)
@@ -58,7 +58,7 @@ $specialPageAliases = array(
        'CreateAccount'             => array( 'Benutzerkonto_anlegen' ),
        'Deadendpages'              => array( 'Sackgassenseiten' ),
        'DeletedContributions'      => array( 'Gelöschte_Beiträge' ),
-       'Diff'                      => array( 'Unterschied' ),
+       'Diff'                      => array( 'Diff', 'Differenz', 'Unterschied' ),
        'DoubleRedirects'           => array( 'Doppelte_Weiterleitungen' ),
        'EditWatchlist'             => array( 'Beobachtungsliste_bearbeiten' ),
        'Emailuser'                 => array( 'E-Mail_senden', 'Mailen', 'E-Mail' ),
index 862a983..2bc7510 100644 (file)
@@ -23,7 +23,7 @@
 require_once __DIR__ . '/Maintenance.php';
 
 /**
- * Maintenance script to dump the SiteStore as a static json file.
+ * Maintenance script to dump a SiteStore as a static json file.
  *
  * @ingroup Maintenance
  */
@@ -32,17 +32,17 @@ class RebuildSitesCache extends Maintenance {
        public function __construct() {
                parent::__construct();
 
-               $this->mDescription = "Dumps site store as json";
+               $this->mDescription = "Cache sites as json for file-based lookup.";
                $this->addOption( 'file', 'File to output the json to', false, true );
        }
 
        public function execute() {
-               $siteListFileCacheBuilder = new SiteListFileCacheBuilder(
-                       SiteSQLStore::newInstance(),
+               $sitesCacheFileBuilder = new SitesCacheFileBuilder(
+                       new DBSiteStore(),
                        $this->getCacheFile()
                );
 
-               $siteListFileCacheBuilder->build();
+               $sitesCacheFileBuilder->build();
        }
 
        /**
@@ -55,7 +55,7 @@ class RebuildSitesCache extends Maintenance {
                        $jsonFile = $this->getConfig()->get( 'SitesCacheFile' );
 
                        if ( $jsonFile === false ) {
-                               $this->error( 'Error: No sites cache file is set in configuration.', 1 );
+                               $this->error( 'Error: No file set in configuration for SitesCacheFile.', 1 );
                        }
                }
 
index 076582b..933212c 100644 (file)
@@ -8,6 +8,7 @@
   },
   "devDependencies": {
     "grunt": "0.4.5",
+    "grunt-cli": "0.1.13",
     "grunt-banana-checker": "0.2.0",
     "grunt-contrib-copy": "0.8.0",
     "grunt-contrib-jshint": "0.11.0",
index b94fa69..2c80a85 100644 (file)
@@ -820,6 +820,11 @@ return array(
                'scripts' => 'resources/src/mediawiki.api/mediawiki.api.login.js',
                'dependencies' => 'mediawiki.api',
        ),
+       'mediawiki.api.options' => array(
+               'scripts' => 'resources/src/mediawiki.api/mediawiki.api.options.js',
+               'dependencies' => 'mediawiki.api',
+               'targets' => array( 'desktop', 'mobile' ),
+       ),
        'mediawiki.api.parse' => array(
                'scripts' => 'resources/src/mediawiki.api/mediawiki.api.parse.js',
                'dependencies' => 'mediawiki.api',
@@ -892,6 +897,15 @@ return array(
                        'feedback-useragent'
                ),
        ),
+
+       'mediawiki.filewarning' => array(
+               'scripts' => 'resources/src/mediawiki/mediawiki.filewarning.js',
+               'styles' => 'resources/src/mediawiki/mediawiki.filewarning.less',
+               'dependencies' => array(
+                       'oojs-ui',
+               ),
+       ),
+
        'mediawiki.helplink' => array(
                'styles' => array(
                        'resources/src/mediawiki/mediawiki.helplink.less',
index 2c99bdd..3a4e145 100644 (file)
@@ -7,5 +7,14 @@
        },
        "ooui-outline-control-move-down": "Færa atriði niður",
        "ooui-outline-control-move-up": "Færa atriði upp",
-       "ooui-toolbar-more": "Fleira"
+       "ooui-outline-control-remove": "Fjarlægja atriði",
+       "ooui-toolbar-more": "Fleira",
+       "ooui-toolgroup-expand": "Fleira",
+       "ooui-toolgroup-collapse": "Færra",
+       "ooui-dialog-message-accept": "Í lagi",
+       "ooui-dialog-message-reject": "Hætta við",
+       "ooui-dialog-process-error": "Eitthvað mistókst",
+       "ooui-dialog-process-dismiss": "Loka",
+       "ooui-dialog-process-retry": "Reyna aftur",
+       "ooui-dialog-process-continue": "Halda áfram"
 }
index 9840319..1d7317b 100644 (file)
@@ -7,5 +7,13 @@
        "ooui-outline-control-move-down": "Элементті төмен жылжыту",
        "ooui-outline-control-move-up": "Элементті жоғары жылжыту",
        "ooui-outline-control-remove": "Элементті алып тастау",
-       "ooui-toolbar-more": "толығырақ"
+       "ooui-toolbar-more": "толығырақ",
+       "ooui-toolgroup-expand": "Тағы",
+       "ooui-toolgroup-collapse": "Азырақ",
+       "ooui-dialog-message-accept": "OK",
+       "ooui-dialog-message-reject": "Қажет емес",
+       "ooui-dialog-process-error": "Бірдеңеден қате кетті",
+       "ooui-dialog-process-dismiss": "Тоқтату",
+       "ooui-dialog-process-retry": "Қайта байқап көріңіз",
+       "ooui-dialog-process-continue": "Жалғастыру"
 }
index 1bec097..ef92e49 100644 (file)
@@ -7,5 +7,13 @@
        "ooui-outline-control-move-down": "Элементни тюбюне кёчюр",
        "ooui-outline-control-move-up": "Элементни башына кёчюр",
        "ooui-outline-control-remove": "Пунктну кетер",
-       "ooui-toolbar-more": "Энтда"
+       "ooui-toolbar-more": "Энтда",
+       "ooui-toolgroup-expand": "Энтда",
+       "ooui-toolgroup-collapse": "Артха",
+       "ooui-dialog-message-accept": "OK",
+       "ooui-dialog-message-reject": "Ызына ал",
+       "ooui-dialog-process-error": "Не эсе да табсыз кетди",
+       "ooui-dialog-process-dismiss": "Джаб",
+       "ooui-dialog-process-retry": "Энтда сынаб кёр",
+       "ooui-dialog-process-continue": "Бардыр"
 }
index 53f1fb0..c975e82 100644 (file)
@@ -4,12 +4,16 @@
                        "Purodha"
                ]
        },
+       "ooui-outline-control-move-down": "Öm eine Plaz noh onge schiehbe",
+       "ooui-outline-control-move-up": "Öm eine Plaz noh bovve schiehbe",
+       "ooui-outline-control-remove": "Dä Plaz läddesch maache → fott domet!",
        "ooui-toolbar-more": "Mih",
        "ooui-toolgroup-expand": "Mih",
        "ooui-toolgroup-collapse": "Winnijer",
        "ooui-dialog-message-accept": "Lohß Jonn!",
        "ooui-dialog-message-reject": "Ophühre",
        "ooui-dialog-process-error": "Öhnsjädd es scheif jejange",
+       "ooui-dialog-process-dismiss": "Maach fott, ha_sch jelässe",
        "ooui-dialog-process-retry": "Norr_ens versöhke",
        "ooui-dialog-process-continue": "Wigger maache"
 }
index c9d0999..7b4d492 100644 (file)
        },
        "ooui-outline-control-move-down": "Ögeyi aşağı taşı",
        "ooui-outline-control-move-up": "Ögeyi yukarı taşı",
+       "ooui-outline-control-remove": "Ögeyi kaldır",
        "ooui-toolbar-more": "Dahası",
        "ooui-toolgroup-expand": "Dahası",
        "ooui-toolgroup-collapse": "Daha az",
-       "ooui-dialog-process-retry": "Tekrar dene"
+       "ooui-dialog-message-accept": "Tamam",
+       "ooui-dialog-message-reject": "İptal",
+       "ooui-dialog-process-error": "Bir şeyler yanlış gitti",
+       "ooui-dialog-process-dismiss": "Kapat",
+       "ooui-dialog-process-retry": "Tekrar dene",
+       "ooui-dialog-process-continue": "Devam et"
 }
index cdf7721..6be1e61 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.9.2
+ * OOjs UI v0.9.3
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2015 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2015-03-12T23:44:05Z
+ * Date: 2015-03-19T23:19:18Z
  */
 .oo-ui-icon-bell {
        background-image: url("themes/mediawiki/images/icons/bell.png");
index f919ec4..2e38c15 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.9.2
+ * OOjs UI v0.9.3
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2015 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2015-03-12T23:44:05Z
+ * Date: 2015-03-19T23:19:18Z
  */
 .oo-ui-icon-article {
        background-image: url("themes/mediawiki/images/icons/article-ltr.png");
index 6c866ab..020ebfb 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.9.2
+ * OOjs UI v0.9.3
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2015 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2015-03-12T23:44:05Z
+ * Date: 2015-03-19T23:19:18Z
  */
 .oo-ui-icon-table {
        background-image: url("themes/mediawiki/images/icons/table.png");
index 37a8562..44d383a 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.9.2
+ * OOjs UI v0.9.3
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2015 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2015-03-12T23:44:05Z
+ * Date: 2015-03-19T23:19:18Z
  */
 .oo-ui-icon-edit {
        background-image: url("themes/mediawiki/images/icons/edit-ltr.png");
index 8bebff8..62f9a65 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.9.2
+ * OOjs UI v0.9.3
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2015 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2015-03-12T23:44:05Z
+ * Date: 2015-03-19T23:19:18Z
  */
 .oo-ui-icon-indent {
        background-image: url("themes/mediawiki/images/icons/indent-ltr.png");
index fdda7c0..1acf7ea 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.9.2
+ * OOjs UI v0.9.3
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2015 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2015-03-12T23:44:05Z
+ * Date: 2015-03-19T23:19:18Z
  */
 .oo-ui-icon-bigger {
        background-image: url("themes/mediawiki/images/icons/bigger-ltr.png");
index e13873f..5497de0 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.9.2
+ * OOjs UI v0.9.3
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2015 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2015-03-12T23:44:05Z
+ * Date: 2015-03-19T23:19:18Z
  */
 .oo-ui-icon-beta {
        background-image: url("themes/mediawiki/images/icons/beta.png");
index b23bdea..ff1c994 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.9.2
+ * OOjs UI v0.9.3
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2015 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2015-03-12T23:44:05Z
+ * Date: 2015-03-19T23:19:18Z
  */
 .oo-ui-icon-stripeFlow {
        background-image: url("themes/mediawiki/images/icons/stripeFlow-ltr.png");
index d200ef2..4d2b8f2 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.9.2
+ * OOjs UI v0.9.3
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2015 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2015-03-12T23:44:05Z
+ * Date: 2015-03-19T23:19:18Z
  */
 .oo-ui-icon-map {
        background-image: url("themes/mediawiki/images/icons/map-ltr.png");
index babb93f..ff8ed55 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.9.2
+ * OOjs UI v0.9.3
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2015 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2015-03-12T23:44:05Z
+ * Date: 2015-03-19T23:19:18Z
  */
 .oo-ui-icon-image {
        background-image: url("themes/mediawiki/images/icons/image-ltr.png");
index 4de7472..f62e46e 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.9.2
+ * OOjs UI v0.9.3
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2015 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2015-03-12T23:44:05Z
+ * Date: 2015-03-19T23:19:18Z
  */
 .oo-ui-icon-block {
        background-image: url("themes/mediawiki/images/icons/block.png");
index b54c204..2f487b3 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.9.2
+ * OOjs UI v0.9.3
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2015 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2015-03-12T23:44:05Z
+ * Date: 2015-03-19T23:19:18Z
  */
 .oo-ui-icon-arrowNext {
        background-image: url("themes/mediawiki/images/icons/arrow-ltr.png");
index 30b4b33..8af9eb1 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.9.2
+ * OOjs UI v0.9.3
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2015 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2015-03-12T23:44:05Z
+ * Date: 2015-03-19T23:19:18Z
  */
 .oo-ui-icon-userActive {
        background-image: url("themes/mediawiki/images/icons/userActive-ltr.png");
index b03fd4d..e8df47a 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.9.2
+ * OOjs UI v0.9.3
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2015 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2015-03-12T23:44:05Z
+ * Date: 2015-03-19T23:19:18Z
  */
 .oo-ui-icon-logoCC {
        background-image: url("themes/mediawiki/images/icons/logo-cc.png");
index 8b82ca2..e38270c 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.9.2
+ * OOjs UI v0.9.3
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2015 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2015-03-12T23:44:05Z
+ * Date: 2015-03-19T23:19:18Z
  */
 .oo-ui-progressBarWidget-slide-frames from {
        margin-left: -40%;
 .oo-ui-textInputWidget-icon {
        width: 2em;
 }
+.oo-ui-textInputWidget.oo-ui-widget-enabled input,
+.oo-ui-textInputWidget.oo-ui-widget-enabled textarea {
+       -webkit-transition: border 0.2s cubic-bezier(0.39, 0.575, 0.565, 1) box-shadow 0.2s cubic-bezier(0.39, 0.575, 0.565, 1);
+          -moz-transition: border 0.2s cubic-bezier(0.39, 0.575, 0.565, 1) box-shadow 0.2s cubic-bezier(0.39, 0.575, 0.565, 1);
+           -ms-transition: border 0.2s cubic-bezier(0.39, 0.575, 0.565, 1) box-shadow 0.2s cubic-bezier(0.39, 0.575, 0.565, 1);
+            -o-transition: border 0.2s cubic-bezier(0.39, 0.575, 0.565, 1) box-shadow 0.2s cubic-bezier(0.39, 0.575, 0.565, 1);
+               transition: border 0.2s cubic-bezier(0.39, 0.575, 0.565, 1) box-shadow 0.2s cubic-bezier(0.39, 0.575, 0.565, 1);
+}
 .oo-ui-textInputWidget.oo-ui-widget-enabled input:focus,
-.oo-ui-textInputWidget.oo-ui-widget-enabled .oo-ui-transition( border 0.2s cubic-bezier(0.39, 0.575, 0.565, 1) box-shadow 0.2s cubic-bezier(0.39, 0.575, 0.565, 1)) textarea:focus {
+.oo-ui-textInputWidget.oo-ui-widget-enabled textarea:focus {
        outline: none;
        border-color: #347bff;
        box-shadow: inset 0 0 0 0.1em #347bff;
index 889b4e1..45872b6 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.9.2
+ * OOjs UI v0.9.3
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2015 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2015-03-12T23:43:45Z
+ * Date: 2015-03-19T23:18:59Z
  */
 /**
  * @class
index b570130..a8afca6 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.9.2
+ * OOjs UI v0.9.3
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2015 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2015-03-12T23:43:45Z
+ * Date: 2015-03-19T23:18:59Z
  */
 ( function ( OO ) {
 
@@ -333,60 +333,61 @@ OO.ui.PendingElement.prototype.popPending = function () {
  *
  *     @example
  *     // Example: An action set used in a process dialog
- *     function ProcessDialog( config ) {
- *         ProcessDialog.super.call( this, config );
+ *     function MyProcessDialog( config ) {
+ *         MyProcessDialog.super.call( this, config );
  *     }
- *     OO.inheritClass( ProcessDialog, OO.ui.ProcessDialog );
- *     ProcessDialog.static.title = 'An action set in a process dialog';
+ *     OO.inheritClass( MyProcessDialog, OO.ui.ProcessDialog );
+ *     MyProcessDialog.static.title = 'An action set in a process dialog';
  *     // An action set that uses modes ('edit' and 'help' mode, in this example).
- *     ProcessDialog.static.actions = [
- *        { action: 'continue', modes: 'edit', label: 'Continue', flags: [ 'primary', 'constructive' ] },
- *        { action: 'help', modes: 'edit', label: 'Help' },
- *        { modes: 'edit', label: 'Cancel', flags: 'safe' },
- *        { action: 'back', modes: 'help', label: 'Back', flags: 'safe' }
+ *     MyProcessDialog.static.actions = [
+ *         { action: 'continue', modes: 'edit', label: 'Continue', flags: [ 'primary', 'constructive' ] },
+ *         { action: 'help', modes: 'edit', label: 'Help' },
+ *         { modes: 'edit', label: 'Cancel', flags: 'safe' },
+ *         { action: 'back', modes: 'help', label: 'Back', flags: 'safe' }
  *     ];
  *
- *     ProcessDialog.prototype.initialize = function () {
- *         ProcessDialog.super.prototype.initialize.apply( this, arguments );
+ *     MyProcessDialog.prototype.initialize = function () {
+ *         MyProcessDialog.super.prototype.initialize.apply( this, arguments );
  *         this.panel1 = new OO.ui.PanelLayout( { padded: true, expanded: false } );
- *         this.panel1.$element.append( '<p>This dialog uses an action set (continue, help, cancel, back) configured with modes. This is edit mode. Click \'help\' to see help mode. </p>' );
+ *         this.panel1.$element.append( '<p>This dialog uses an action set (continue, help, cancel, back) configured with modes. This is edit mode. Click \'help\' to see help mode.</p>' );
  *         this.panel2 = new OO.ui.PanelLayout( { padded: true, expanded: false } );
- *         this.panel2.$element.append( '<p>This is help mode. Only the \'back\' action widget is configured to be visible here. Click \'back\' to return to \'edit\' mode</p>' );
- *         this.stackLayout= new OO.ui.StackLayout( {
+ *         this.panel2.$element.append( '<p>This is help mode. Only the \'back\' action widget is configured to be visible here. Click \'back\' to return to \'edit\' mode.</p>' );
+ *         this.stackLayout = new OO.ui.StackLayout( {
  *             items: [ this.panel1, this.panel2 ]
- *         });
+ *         } );
  *         this.$body.append( this.stackLayout.$element );
  *     };
- *     ProcessDialog.prototype.getSetupProcess = function ( data ) {
- *         return ProcessDialog.super.prototype.getSetupProcess.call( this, data )
- *         .next( function () {
- *         this.actions.setMode('edit');
- *         }, this );
+ *     MyProcessDialog.prototype.getSetupProcess = function ( data ) {
+ *         return MyProcessDialog.super.prototype.getSetupProcess.call( this, data )
+ *             .next( function () {
+ *                 this.actions.setMode( 'edit' );
+ *             }, this );
  *     };
- *     ProcessDialog.prototype.getActionProcess = function ( action ) {
+ *     MyProcessDialog.prototype.getActionProcess = function ( action ) {
  *         if ( action === 'help' ) {
  *             this.actions.setMode( 'help' );
  *             this.stackLayout.setItem( this.panel2 );
- *             } else if ( action === 'back' ) {
+ *         } else if ( action === 'back' ) {
  *             this.actions.setMode( 'edit' );
  *             this.stackLayout.setItem( this.panel1 );
- *             } else if ( action === 'continue' ) {
+ *         } else if ( action === 'continue' ) {
  *             var dialog = this;
  *             return new OO.ui.Process( function () {
  *                 dialog.close();
  *             } );
  *         }
- *         return ProcessDialog.super.prototype.getActionProcess.call( this, action );
+ *         return MyProcessDialog.super.prototype.getActionProcess.call( this, action );
  *     };
- *     ProcessDialog.prototype.getBodyHeight = function () {
+ *     MyProcessDialog.prototype.getBodyHeight = function () {
  *         return this.panel1.$element.outerHeight( true );
  *     };
  *     var windowManager = new OO.ui.WindowManager();
  *     $( 'body' ).append( windowManager.$element );
- *     var processDialog = new ProcessDialog({
- *        size: 'medium'});
- *     windowManager.addWindows( [ processDialog ] );
- *     windowManager.openWindow( processDialog );
+ *     var dialog = new MyProcessDialog( {
+ *         size: 'medium'
+ *     } );
+ *     windowManager.addWindows( [ dialog ] );
+ *     windowManager.openWindow( dialog );
  *
  * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Process_Dialogs#Action_sets
  *
@@ -1614,7 +1615,8 @@ OO.mixinClass( OO.ui.Layout, OO.EventEmitter );
  *
  * @constructor
  * @param {Object} [config] Configuration options
- * @cfg {boolean} [disabled=false] Disable
+ * @cfg {boolean} [disabled=false] Disable the widget. Disabled widgets cannot be used and their
+ *  appearance reflects this state.
  */
 OO.ui.Widget = function OoUiWidget( config ) {
        // Initialize config
@@ -1644,11 +1646,17 @@ OO.mixinClass( OO.ui.Widget, OO.EventEmitter );
 
 /**
  * @event disable
+ *
+ * A 'disable' event is emitted when a widget is disabled.
+ *
  * @param {boolean} disabled Widget is disabled
  */
 
 /**
  * @event toggle
+ *
+ * A 'toggle' event is emitted when the visibility of the widget changes.
+ *
  * @param {boolean} visible Widget is visible
  */
 
@@ -1657,16 +1665,16 @@ OO.mixinClass( OO.ui.Widget, OO.EventEmitter );
 /**
  * Check if the widget is disabled.
  *
- * @return {boolean} Button is disabled
+ * @return {boolean} Widget is disabled
  */
 OO.ui.Widget.prototype.isDisabled = function () {
        return this.disabled;
 };
 
 /**
- * Set the disabled state of the widget.
+ * Set the 'disabled' state of the widget.
  *
- * This should probably change the widgets' appearance and prevent it from being used.
+ * When a widget is disabled, it cannot be used and its appearance is updated to reflect this state.
  *
  * @param {boolean} disabled Disable widget
  * @chainable
@@ -2103,9 +2111,10 @@ OO.ui.Window.prototype.updateSize = function () {
 };
 
 /**
- * Set window dimensions.
+ * Set window dimensions. This method is called by the {@link OO.ui.WindowManager window manager}
+ * when the window is opening. In general, setDimensions should not be called directly.
  *
- * Properties are applied to the frame container.
+ * To set the size of the window, use the #setSize method.
  *
  * @param {Object} dim CSS dimension properties
  * @param {string|number} [dim.width] Width
@@ -2539,9 +2548,10 @@ OO.ui.Dialog.prototype.getActionProcess = function ( action ) {
  * @inheritdoc
  *
  * @param {Object} [data] Dialog opening data
- * @param {jQuery|string|Function|null} [data.title] Dialog title, omit to use #static-title
- * @param {Object[]} [data.actions] List of OO.ui.ActionWidget configuration options for each
- *   action item, omit to use #static-actions
+ * @param {jQuery|string|Function|null} [data.title] Dialog title, omit to use
+ *  the {@link #static-title static title}
+ * @param {Object[]} [data.actions] List of configuration options for each
+ *   {@link OO.ui.ActionWidget action widget}, omit to use {@link #static-actions static actions}.
  */
 OO.ui.Dialog.prototype.getSetupProcess = function ( data ) {
        data = data || {};
@@ -2549,20 +2559,13 @@ OO.ui.Dialog.prototype.getSetupProcess = function ( data ) {
        // Parent method
        return OO.ui.Dialog.super.prototype.getSetupProcess.call( this, data )
                .next( function () {
-                       var i, len,
-                               items = [],
-                               config = this.constructor.static,
+                       var config = this.constructor.static,
                                actions = data.actions !== undefined ? data.actions : config.actions;
 
                        this.title.setLabel(
                                data.title !== undefined ? data.title : this.constructor.static.title
                        );
-                       for ( i = 0, len = actions.length; i < len; i++ ) {
-                               items.push(
-                                       new OO.ui.ActionWidget( actions[ i ] )
-                               );
-                       }
-                       this.actions.add( items );
+                       this.actions.add( this.getActionWidgets( actions ) );
 
                        if ( this.constructor.static.escapable ) {
                                this.$document.on( 'keydown', this.onDocumentKeyDownHandler );
@@ -2601,6 +2604,22 @@ OO.ui.Dialog.prototype.initialize = function () {
        this.setPendingElement( this.$head );
 };
 
+/**
+ * Get action widgets from a list of configs
+ *
+ * @param {Object[]} actions Action widget configs
+ * @return {OO.ui.ActionWidget[]} Action widgets
+ */
+OO.ui.Dialog.prototype.getActionWidgets = function ( actions ) {
+       var i, len, widgets = [];
+       for ( i = 0, len = actions.length; i < len; i++ ) {
+               widgets.push(
+                       new OO.ui.ActionWidget( actions[ i ] )
+               );
+       }
+       return widgets;
+};
+
 /**
  * Attach action actions.
  *
@@ -2692,6 +2711,8 @@ OO.ui.Dialog.prototype.executeAction = function ( action ) {
  * @constructor
  * @param {Object} [config] Configuration options
  * @cfg {OO.Factory} [factory] Window factory to use for automatic instantiation
+ *  Note that window classes that are instantiated with a factory must have
+ *  a {@link OO.ui.Dialog#static-name static name} property that specifies a symbolic name.
  * @cfg {boolean} [modal=true] Prevent interaction outside the dialog
  */
 OO.ui.WindowManager = function OoUiWindowManager( config ) {
@@ -2734,34 +2755,30 @@ OO.mixinClass( OO.ui.WindowManager, OO.EventEmitter );
 /* Events */
 
 /**
- * Window is opening.
- *
- * Fired when the window begins to be opened.
+ * An 'opening' event is emitted when the window begins to be opened.
  *
  * @event opening
  * @param {OO.ui.Window} win Window that's being opened
- * @param {jQuery.Promise} opening Promise resolved when window is opened; when the promise is
- *   resolved the first argument will be a promise which will be resolved when the window begins
- *   closing, the second argument will be the opening data; progress notifications will be fired on
- *   the promise for `setup` and `ready` when those processes are completed respectively.
+ * @param {jQuery.Promise} opening An `opening` promise resolved with a value when the window is opened successfully.
+ *  When the `opening` promise is resolved, the first argument of the value is an 'opened' promise, the second argument
+ *  is the opening data. The `opening` promise emits `setup` and `ready` notifications when those processes are complete.
  * @param {Object} data Window opening data
  */
 
 /**
- * Window is closing.
- *
- * Fired when the window begins to be closed.
+ * A 'closing' event is emitted when the window begins to be closed.
  *
  * @event closing
  * @param {OO.ui.Window} win Window that's being closed
- * @param {jQuery.Promise} closing Promise resolved when window is closed; when the promise
- *   is resolved the first argument will be a the closing data; progress notifications will be fired
- *   on the promise for `hold` and `teardown` when those processes are completed respectively.
+ * @param {jQuery.Promise} closing A `closing` promise is resolved with a value when the window
+ *  is closed successfully. The promise emits `hold` and `teardown` notifications when those
+ *  processes are complete. When the `closing` promise is resolved, the first argument of its value
+ *  is the closing data.
  * @param {Object} data Window closing data
  */
 
 /**
- * Window was resized.
+ * A 'resize' event is emitted when a window is resized.
  *
  * @event resize
  * @param {OO.ui.Window} win Window that was resized
@@ -2770,7 +2787,7 @@ OO.mixinClass( OO.ui.WindowManager, OO.EventEmitter );
 /* Static Properties */
 
 /**
- * Map of symbolic size names and CSS properties.
+ * Map of the symbolic name of each window size and its CSS properties.
  *
  * @static
  * @inheritable
@@ -2797,9 +2814,9 @@ OO.ui.WindowManager.static.sizes = {
 };
 
 /**
- * Symbolic name of default size.
+ * Symbolic name of the default window size.
  *
- * Default size is used if the window's requested size is not recognized.
+ * The default size is used if the window's requested size is not recognized.
  *
  * @static
  * @inheritable
@@ -2812,6 +2829,7 @@ OO.ui.WindowManager.static.defaultSize = 'medium';
 /**
  * Handle window resize events.
  *
+ * @private
  * @param {jQuery.Event} e Window resize event
  */
 OO.ui.WindowManager.prototype.onWindowResize = function () {
@@ -2822,6 +2840,7 @@ OO.ui.WindowManager.prototype.onWindowResize = function () {
 /**
  * Handle window resize events.
  *
+ * @private
  * @param {jQuery.Event} e Window resize event
  */
 OO.ui.WindowManager.prototype.afterWindowResize = function () {
@@ -2876,7 +2895,7 @@ OO.ui.WindowManager.prototype.hasWindow = function ( win ) {
 };
 
 /**
- * Get the number of milliseconds to wait between beginning opening and executing setup process.
+ * Get the number of milliseconds to wait after opening begins before executing the ‘setup’ process.
  *
  * @param {OO.ui.Window} win Window being opened
  * @param {Object} [data] Window opening data
@@ -2887,7 +2906,7 @@ OO.ui.WindowManager.prototype.getSetupDelay = function () {
 };
 
 /**
- * Get the number of milliseconds to wait between finishing setup and executing ready process.
+ * Get the number of milliseconds to wait after setup has finished before executing the ‘ready’ process.
  *
  * @param {OO.ui.Window} win Window being opened
  * @param {Object} [data] Window opening data
@@ -2898,7 +2917,7 @@ OO.ui.WindowManager.prototype.getReadyDelay = function () {
 };
 
 /**
- * Get the number of milliseconds to wait between beginning closing and executing hold process.
+ * Get the number of milliseconds to wait after closing has begun before executing the 'hold' process.
  *
  * @param {OO.ui.Window} win Window being closed
  * @param {Object} [data] Window closing data
@@ -2909,7 +2928,8 @@ OO.ui.WindowManager.prototype.getHoldDelay = function () {
 };
 
 /**
- * Get the number of milliseconds to wait between finishing hold and executing teardown process.
+ * Get the number of milliseconds to wait after the ‘hold’ process has finished before
+ * executing the ‘teardown’ process.
  *
  * @param {OO.ui.Window} win Window being closed
  * @param {Object} [data] Window closing data
@@ -2920,14 +2940,17 @@ OO.ui.WindowManager.prototype.getTeardownDelay = function () {
 };
 
 /**
- * Get managed window by symbolic name.
+ * Get a window by its symbolic name.
  *
- * If window is not yet instantiated, it will be instantiated and added automatically.
+ * If the window is not yet instantiated and its symbolic name is recognized by a factory, it will be
+ * instantiated and added to the window manager automatically. Please see the [OOjs UI documentation on MediaWiki][3]
+ * for more information about using factories.
+ * [3]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Window_managers
  *
- * @param {string} name Symbolic window name
+ * @param {string} name Symbolic name of the window
  * @return {jQuery.Promise} Promise resolved with matching window, or rejected with an OO.ui.Error
- * @throws {Error} If the symbolic name is unrecognized by the factory
- * @throws {Error} If the symbolic name unrecognized as a managed window
+ * @throws {Error} An error is thrown if the symbolic name is not recognized by the factory.
+ * @throws {Error} An error is thrown if the named window is not recognized as a managed window.
  */
 OO.ui.WindowManager.prototype.getWindow = function ( name ) {
        var deferred = $.Deferred(),
@@ -2970,8 +2993,8 @@ OO.ui.WindowManager.prototype.getCurrentWindow = function () {
  *
  * @param {OO.ui.Window|string} win Window object or symbolic name of window to open
  * @param {Object} [data] Window opening data
- * @return {jQuery.Promise} Promise resolved when window is done opening; see {@link #event-opening}
- *   for more details about the `opening` promise
+ * @return {jQuery.Promise} An `opening` promise resolved when the window is done opening.
+ *  See {@link #event-opening 'opening' event}  for more information about `opening` promises.
  * @fires opening
  */
 OO.ui.WindowManager.prototype.openWindow = function ( win, data ) {
@@ -3035,9 +3058,9 @@ OO.ui.WindowManager.prototype.openWindow = function ( win, data ) {
  *
  * @param {OO.ui.Window|string} win Window object or symbolic name of window to close
  * @param {Object} [data] Window closing data
- * @return {jQuery.Promise} Promise resolved when window is done closing; see {@link #event-closing}
- *   for more details about the `closing` promise
- * @throws {Error} If no window by that name is being managed
+ * @return {jQuery.Promise} A `closing` promise resolved when the window is done closing.
+ *  See {@link #event-closing 'closing' event} for more information about closing promises.
+ * @throws {Error} An error is thrown if the window is not managed by the window manager.
  * @fires closing
  */
 OO.ui.WindowManager.prototype.closeWindow = function ( win, data ) {
@@ -3103,11 +3126,16 @@ OO.ui.WindowManager.prototype.closeWindow = function ( win, data ) {
 };
 
 /**
- * Add windows.
+ * Add windows to the window manager.
  *
- * @param {Object.<string,OO.ui.Window>|OO.ui.Window[]} windows Windows to add
- * @throws {Error} If one of the windows being added without an explicit symbolic name does not have
- *   a statically configured symbolic name
+ * Windows can be added by reference, symbolic name, or explicitly defined symbolic names.
+ * See the [OOjs ui documentation on MediaWiki] [2] for examples.
+ * [2]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Window_managers
+ *
+ * @param {Object.<string,OO.ui.Window>|OO.ui.Window[]} windows An array of window objects specified
+ *  by reference, symbolic name, or explicitly defined symbolic names.
+ * @throws {Error} An error is thrown if a window is added by symbolic name, but has neither an
+ *  explicit nor a statically configured symbolic name.
  */
 OO.ui.WindowManager.prototype.addWindows = function ( windows ) {
        var i, len, win, name, list;
@@ -3136,13 +3164,15 @@ OO.ui.WindowManager.prototype.addWindows = function ( windows ) {
 };
 
 /**
- * Remove windows.
+ * Remove the specified windows from the windows manager.
  *
- * Windows will be closed before they are removed.
+ * Windows will be closed before they are removed. If you wish to remove all windows, you may wish to use
+ * the #clearWindows method instead. If you no longer need the window manager and want to ensure that it no
+ * longer listens to events, use the #destroy method.
  *
  * @param {string[]} names Symbolic names of windows to remove
  * @return {jQuery.Promise} Promise resolved when window is closed and removed
- * @throws {Error} If windows being removed are not being managed
+ * @throws {Error} An error is thrown if the named windows are not managed by the window manager.
  */
 OO.ui.WindowManager.prototype.removeWindows = function ( names ) {
        var i, len, win, name, cleanupWindow,
@@ -3167,9 +3197,11 @@ OO.ui.WindowManager.prototype.removeWindows = function ( names ) {
 };
 
 /**
- * Remove all windows.
+ * Remove all windows from the window manager.
  *
- * Windows will be closed before they are removed.
+ * Windows will be closed before they are removed. Note that the window manager, though not in use, will still
+ * listen to events. If the window manager will not be used again, you may wish to use the #destroy method instead.
+ * To remove just a subset of windows, use the #removeWindows method.
  *
  * @return {jQuery.Promise} Promise resolved when all windows are closed and removed
  */
@@ -3178,7 +3210,7 @@ OO.ui.WindowManager.prototype.clearWindows = function () {
 };
 
 /**
- * Set dialog size.
+ * Set dialog size. In general, this method should not be called directly.
  *
  * Fullscreen mode will be used if the dialog is too wide to fit in the screen.
  *
@@ -3213,6 +3245,7 @@ OO.ui.WindowManager.prototype.updateWindowSize = function ( win ) {
 /**
  * Bind or unbind global events for scrolling.
  *
+ * @private
  * @param {boolean} [on] Bind global events
  * @chainable
  */
@@ -3255,6 +3288,7 @@ OO.ui.WindowManager.prototype.toggleGlobalEvents = function ( on ) {
 /**
  * Toggle screen reader visibility of content other than the window manager.
  *
+ * @private
  * @param {boolean} [isolate] Make only the window manager visible to screen readers
  * @chainable
  */
@@ -3279,7 +3313,11 @@ OO.ui.WindowManager.prototype.toggleAriaIsolation = function ( isolate ) {
 };
 
 /**
- * Destroy window manager.
+ * Destroy the window manager.
+ *
+ * Destroying the window manager ensures that it will no longer listen to events. If you would like to
+ * continue using the window manager, but wish to remove all windows from it, use the #clearWindows method
+ * instead.
  */
 OO.ui.WindowManager.prototype.destroy = function () {
        this.toggleGlobalEvents( false );
@@ -3764,20 +3802,20 @@ OO.ui.Theme.prototype.updateElementClasses = function ( element ) {
  *     // TabIndexedElement is mixed into the ButtonWidget class
  *     // to provide a tabIndex property.
  *     var button1 = new OO.ui.ButtonWidget( {
- *         label : 'fourth',
- *         tabIndex : 4
+ *         label: 'fourth',
+ *         tabIndex: 4
  *     } );
  *     var button2 = new OO.ui.ButtonWidget( {
- *         label : 'second',
- *         tabIndex : 2
+ *         label: 'second',
+ *         tabIndex: 2
  *     } );
  *     var button3 = new OO.ui.ButtonWidget( {
- *         label : 'third',
- *         tabIndex : 3
+ *         label: 'third',
+ *         tabIndex: 3
  *     } );
  *     var button4 = new OO.ui.ButtonWidget( {
- *         label : 'first',
- *         tabIndex : 1
+ *         label: 'first',
+ *         tabIndex: 1
  *     } );
  *     $( 'body' ).append( button1.$element, button2.$element, button3.$element, button4.$element );
  *
@@ -3786,9 +3824,12 @@ OO.ui.Theme.prototype.updateElementClasses = function ( element ) {
  *
  * @constructor
  * @param {Object} [config] Configuration options
- * @cfg {jQuery} [$tabIndexed] tabIndexed node, assigned to #$tabIndexed, omit to use #$element
- * @cfg {number|null} [tabIndex=0] Tab index value. Use 0 to use default ordering, use -1 to
- *  prevent tab focusing, use null to suppress the `tabindex` attribute.
+ * @cfg {jQuery} [$tabIndexed] The element that should use the tabindex functionality. By default,
+ *  the functionality is applied to the element created by the class ($element). If a different element is specified, the tabindex
+ *  functionality will be applied to it instead.
+ * @cfg {number|null} [tabIndex=0] Number that specifies the element’s position in the tab-navigation
+ *  order (e.g., 1 for the first focusable element). Use 0 to use the default navigation order; use -1
+ *  to remove the element from the tab-navigation flow.
  */
 OO.ui.TabIndexedElement = function OoUiTabIndexedElement( config ) {
        // Configuration initialization
@@ -3813,11 +3854,13 @@ OO.initClass( OO.ui.TabIndexedElement );
 /* Methods */
 
 /**
- * Set the element with `tabindex` attribute.
+ * Set the element that should use the tabindex functionality.
  *
- * If an element is already set, it will be cleaned up before setting up the new element.
+ * This method is used to retarget a tabindex mixin so that its functionality applies
+ * to the specified element. If an element is currently using the functionality, the mixin’s
+ * effect on that element is removed before the new element is set up.
  *
- * @param {jQuery} $tabIndexed Element to set tab index on
+ * @param {jQuery} $tabIndexed Element that should use the tabindex functionality
  * @chainable
  */
 OO.ui.TabIndexedElement.prototype.setTabIndexedElement = function ( $tabIndexed ) {
@@ -3831,9 +3874,9 @@ OO.ui.TabIndexedElement.prototype.setTabIndexedElement = function ( $tabIndexed
 };
 
 /**
- * Set tab index value.
+ * Set the value of the tabindex.
  *
- * @param {number|null} tabIndex Tab index value or null for no tab index
+ * @param {number|null} tabIndex Tabindex value, or `null` for no tabindex
  * @chainable
  */
 OO.ui.TabIndexedElement.prototype.setTabIndex = function ( tabIndex ) {
@@ -3880,9 +3923,9 @@ OO.ui.TabIndexedElement.prototype.onDisable = function () {
 };
 
 /**
- * Get tab index value.
+ * Get the value of the tabindex.
  *
- * @return {number|null} Tab index value
+ * @return {number|null} Tabindex value
  */
 OO.ui.TabIndexedElement.prototype.getTabIndex = function () {
        return this.tabIndex;
@@ -4149,7 +4192,8 @@ OO.ui.ButtonElement.prototype.setActive = function ( value ) {
  *
  * @constructor
  * @param {Object} [config] Configuration options
- * @cfg {jQuery} [$group] Container node, assigned to #$group, omit to use a generated `<div>`
+ * @cfg {jQuery} [$group] The container element created by the class. If this configuration
+ *  is omitted, the group element will use a generated `<div>`.
  */
 OO.ui.GroupElement = function OoUiGroupElement( config ) {
        // Configuration initialization
@@ -4183,7 +4227,7 @@ OO.ui.GroupElement.prototype.setGroupElement = function ( $group ) {
 };
 
 /**
- * Check if there are no items.
+ * Check if a group contains no items.
  *
  * @return {boolean} Group is empty
  */
@@ -4192,9 +4236,13 @@ OO.ui.GroupElement.prototype.isEmpty = function () {
 };
 
 /**
- * Get items.
+ * Get all items in the group.
+ *
+ * The method returns an array of item references (e.g., [button1, button2, button3]) and is useful
+ * when synchronizing groups of items, or whenever the references are required (e.g., when removing items
+ * from a group).
  *
- * @return {OO.ui.Element[]} Items
+ * @return {OO.ui.Element[]} An array of items.
  */
 OO.ui.GroupElement.prototype.getItems = function () {
        return this.items.slice( 0 );
@@ -4203,7 +4251,8 @@ OO.ui.GroupElement.prototype.getItems = function () {
 /**
  * Get an item by its data.
  *
- * Data is compared by a hash of its value. Only the first item with matching data will be returned.
+ * Only the first item with matching data will be returned. To return all matching items,
+ * use the #getItemsFromData method.
  *
  * @param {Object} data Item data to search for
  * @return {OO.ui.Element|null} Item with equivalent data, `null` if none exists
@@ -4225,7 +4274,7 @@ OO.ui.GroupElement.prototype.getItemFromData = function ( data ) {
 /**
  * Get items by their data.
  *
- * Data is compared by a hash of its value. All items with matching data will be returned.
+ * All items with matching data will be returned. To return only the first match, use the #getItemFromData method instead.
  *
  * @param {Object} data Item data to search for
  * @return {OO.ui.Element[]} Items with equivalent data
@@ -4246,15 +4295,18 @@ OO.ui.GroupElement.prototype.getItemsFromData = function ( data ) {
 };
 
 /**
- * Add an aggregate item event.
+ * Aggregate the events emitted by the group.
  *
- * Aggregated events are listened to on each item and then emitted by the group under a new name,
- * and with an additional leading parameter containing the item that emitted the original event.
- * Other arguments that were emitted from the original event are passed through.
+ * When events are aggregated, the group will listen to all contained items for the event,
+ * and then emit the event under a new name. The new event will contain an additional leading
+ * parameter containing the item that emitted the original event. Other arguments emitted from
+ * the original event are passed through.
  *
- * @param {Object.<string,string|null>} events Aggregate events emitted by group, keyed by item
- *   event, use null value to remove aggregation
- * @throws {Error} If aggregation already exists
+ * @param {Object.<string,string|null>} events An object keyed by the name of the event that should be
+ *  aggregated  (e.g., ‘click’) and the value of the new name to use (e.g., ‘groupClick’).
+ *  A `null` value will remove aggregated events.
+
+ * @throws {Error} An error is thrown if aggregation already exists.
  */
 OO.ui.GroupElement.prototype.aggregate = function ( events ) {
        var i, len, item, add, remove, itemEvent, groupEvent;
@@ -4299,12 +4351,13 @@ OO.ui.GroupElement.prototype.aggregate = function ( events ) {
 };
 
 /**
- * Add items.
+ * Add items to the group.
  *
- * Adding an existing item will move it.
+ * Items will be added to the end of the group array unless the optional `index` parameter specifies
+ * a different insertion point. Adding an existing item will move it to the end of the array or the point specified by the `index`.
  *
- * @param {OO.ui.Element[]} items Items
- * @param {number} [index] Index to insert items at
+ * @param {OO.ui.Element[]} items An array of items to add to the group
+ * @param {number} [index] Index of the insertion point
  * @chainable
  */
 OO.ui.GroupElement.prototype.addItems = function ( items, index ) {
@@ -4350,11 +4403,12 @@ OO.ui.GroupElement.prototype.addItems = function ( items, index ) {
 };
 
 /**
- * Remove items.
+ * Remove the specified items from a group.
  *
- * Items will be detached, not removed, so they can be used later.
+ * Removed items are detached (not removed) from the DOM so that they may be reused.
+ * To remove all items from a group, you may wish to use the #clearItems method instead.
  *
- * @param {OO.ui.Element[]} items Items to remove
+ * @param {OO.ui.Element[]} items An array of items to remove
  * @chainable
  */
 OO.ui.GroupElement.prototype.removeItems = function ( items ) {
@@ -4385,9 +4439,10 @@ OO.ui.GroupElement.prototype.removeItems = function ( items ) {
 };
 
 /**
- * Clear all items.
+ * Clear all items from the group.
  *
- * Items will be detached, not removed, so they can be used later.
+ * Cleared items are detached from the DOM, not removed, so that they may be reused.
+ * To remove only a subset of items from a group, use the #removeItems method.
  *
  * @chainable
  */
@@ -4565,11 +4620,14 @@ OO.ui.DraggableElement.prototype.getIndex = function () {
  *
  * @abstract
  * @class
+ * @mixins OO.ui.GroupElement
  *
  * @constructor
  * @param {Object} [config] Configuration options
- * @cfg {jQuery} [$group] Container node, assigned to #$group, omit to use a generated `<div>`
- * @cfg {string} [orientation] Item orientation, 'horizontal' or 'vertical'. Defaults to 'vertical'
+ * @cfg {string} [orientation] Item orientation: 'horizontal' or 'vertical'. The orientation
+ *  should match the layout of the items. Items displayed in a single row
+ *  or in several rows should use horizontal orientation. The vertical orientation should only be
+ *  used when the items are displayed in a single column. Defaults to 'vertical'
  */
 OO.ui.DraggableGroupElement = function OoUiDraggableGroupElement( config ) {
        // Configuration initialization
@@ -4620,6 +4678,8 @@ OO.mixinClass( OO.ui.DraggableGroupElement, OO.ui.GroupElement );
 /* Events */
 
 /**
+ * A 'reorder' event is emitted when the order of items in the group changes.
+ *
  * @event reorder
  * @param {OO.ui.DraggableElement} item Reordered item
  * @param {number} [newIndex] New index for the item
@@ -4629,6 +4689,8 @@ OO.mixinClass( OO.ui.DraggableGroupElement, OO.ui.GroupElement );
 
 /**
  * Respond to item drag start event
+ *
+ * @private
  * @param {OO.ui.DraggableElement} item Dragged item
  */
 OO.ui.DraggableGroupElement.prototype.onItemDragStart = function ( item ) {
@@ -4657,6 +4719,8 @@ OO.ui.DraggableGroupElement.prototype.onItemDragStart = function ( item ) {
 
 /**
  * Respond to item drag end event
+ *
+ * @private
  */
 OO.ui.DraggableGroupElement.prototype.onItemDragEnd = function () {
        this.unsetDragItem();
@@ -4665,6 +4729,8 @@ OO.ui.DraggableGroupElement.prototype.onItemDragEnd = function () {
 
 /**
  * Handle drop event and switch the order of the items accordingly
+ *
+ * @private
  * @param {OO.ui.DraggableElement} item Dropped item
  * @fires reorder
  */
@@ -4689,6 +4755,8 @@ OO.ui.DraggableGroupElement.prototype.onItemDrop = function ( item ) {
 
 /**
  * Handle dragleave event.
+ *
+ * @private
  */
 OO.ui.DraggableGroupElement.prototype.onDragLeave = function () {
        // This means the item was dragged outside the widget
@@ -4699,6 +4767,8 @@ OO.ui.DraggableGroupElement.prototype.onDragLeave = function () {
 
 /**
  * Respond to dragover event
+ *
+ * @private
  * @param {jQuery.Event} event Event details
  */
 OO.ui.DraggableGroupElement.prototype.onDragOver = function ( e ) {
@@ -4770,6 +4840,7 @@ OO.ui.DraggableGroupElement.prototype.onDragOver = function ( e ) {
 
 /**
  * Set a dragged item
+ *
  * @param {OO.ui.DraggableElement} item Dragged item
  */
 OO.ui.DraggableGroupElement.prototype.setDragItem = function ( item ) {
@@ -4787,15 +4858,17 @@ OO.ui.DraggableGroupElement.prototype.unsetDragItem = function () {
 };
 
 /**
- * Get the current dragged item
- * @return {OO.ui.DraggableElement|null} item Dragged item or null if no item is dragged
+ * Get the item that is currently being dragged.
+ *
+ * @return {OO.ui.DraggableElement|null} The currently dragged item, or `null` if no item is being dragged
  */
 OO.ui.DraggableGroupElement.prototype.getDragItem = function () {
        return this.dragItem;
 };
 
 /**
- * Check if there's an item being dragged.
+ * Check if an item in the group is currently being dragged.
+ *
  * @return {Boolean} Item is being dragged
  */
 OO.ui.DraggableGroupElement.prototype.isDragging = function () {
@@ -5085,9 +5158,9 @@ OO.ui.IndicatorElement.prototype.setIndicatorElement = function ( $indicator ) {
 };
 
 /**
- * Set indicator name.
+ * Set the indicator by its symbolic name: ‘alert’, ‘down’, ‘next’, ‘previous’, ‘required’, ‘up’. Use `null` to remove the indicator.
  *
- * @param {string|null} indicator Symbolic name of indicator to use or null for no indicator
+ * @param {string|null} indicator Symbolic name of indicator, or `null` for no indicator
  * @chainable
  */
 OO.ui.IndicatorElement.prototype.setIndicator = function ( indicator ) {
@@ -5112,10 +5185,12 @@ OO.ui.IndicatorElement.prototype.setIndicator = function ( indicator ) {
 };
 
 /**
- * Set indicator title.
+ * Set the indicator title.
+ *
+ * The title is displayed when a user moves the mouse over the indicator.
  *
- * @param {string|Function|null} indicator Indicator title text, a function that returns text or
- *   null for no indicator title
+ * @param {string|Function|null} indicator Indicator title text, a function that returns text, or
+ *   `null` for no indicator title
  * @chainable
  */
 OO.ui.IndicatorElement.prototype.setIndicatorTitle = function ( indicatorTitle ) {
@@ -5138,7 +5213,7 @@ OO.ui.IndicatorElement.prototype.setIndicatorTitle = function ( indicatorTitle )
 };
 
 /**
- * Get indicator name.
+ * Get the symbolic name of the indicator (e.g., ‘alert’ or  ‘down’).
  *
  * @return {string} Symbolic name of indicator
  */
@@ -5147,7 +5222,9 @@ OO.ui.IndicatorElement.prototype.getIndicator = function () {
 };
 
 /**
- * Get indicator title.
+ * Get the indicator title.
+ *
+ * The title is displayed when a user moves the mouse over the indicator.
  *
  * @return {string} Indicator title text
  */
@@ -5322,6 +5399,8 @@ OO.ui.LabelElement.prototype.setLabelContent = function ( label ) {
  * @param {Object} [config] Configuration options
  * @cfg {jQuery} [$overlay] Overlay for dropdown; defaults to relative positioning
  * @cfg {jQuery} [$container=this.$element] Element to render menu under
+ * @cfg {boolean} [allowSuggestionsWhenEmpty=false] Whether suggestions will be requested
+ *   and shown when the user has not typed anything yet.
  */
 OO.ui.LookupElement = function OoUiLookupElement( config ) {
        // Configuration initialization
@@ -5334,6 +5413,9 @@ OO.ui.LookupElement = function OoUiLookupElement( config ) {
                input: this,
                $container: config.$container
        } );
+
+       this.allowSuggestionsWhenEmpty = config.allowSuggestionsWhenEmpty || false;
+
        this.lookupCache = {};
        this.lookupQuery = null;
        this.lookupRequest = null;
@@ -5492,8 +5574,8 @@ OO.ui.LookupElement.prototype.populateLookupMenu = function () {
                return;
        }
 
-       // If the input is empty, clear the menu
-       if ( value === '' ) {
+       // If the input is empty, clear the menu, unless suggestions when empty are allowed.
+       if ( !this.allowSuggestionsWhenEmpty && value === '' ) {
                this.closeLookupMenu();
        // Skip population if there is already a request pending for the current value
        } else if ( value !== this.lookupQuery ) {
@@ -5685,6 +5767,24 @@ OO.ui.PopupElement.prototype.getPopup = function () {
  * - **destructive**: Destructive styling is applied to convey that the widget will remove something.
  * - **constructive**: Constructive styling is applied to convey that the widget will create something.
  *
+ * The flags affect the appearance of the buttons:
+ *
+ *     @example
+ *     // FlaggedElement is mixed into ButtonWidget to provide styling flags
+ *     var button1 = new OO.ui.ButtonWidget( {
+ *         label: 'Constructive',
+ *         flags: 'constructive'
+ *     } );
+ *     var button2 = new OO.ui.ButtonWidget( {
+ *         label: 'Destructive',
+ *         flags: 'destructive'
+ *     } );
+ *     var button3 = new OO.ui.ButtonWidget( {
+ *         label: 'Progressive',
+ *         flags: 'progressive'
+ *     } );
+ *     $( 'body' ).append( button1.$element, button2.$element, button3.$element );
+ *
  * {@link OO.ui.ActionWidget ActionWidgets}, which are a special kind of button that execute an action, use these flags: **primary** and **safe**.
  * Please see the [OOjs UI documentation on MediaWiki] [1] for more information.
  *
@@ -5698,7 +5798,9 @@ OO.ui.PopupElement.prototype.getPopup = function () {
  * @cfg {string|string[]} [flags] The name or names of the flags (e.g., 'constructive' or 'primary') to apply.
  *  Please see the [OOjs UI documentation on MediaWiki] [2] for more information about available flags.
  *  [2]: https://www.mediawiki.org/wiki/OOjs_UI/Elements/Flagged
- * @cfg {jQuery} [$flagged] Flagged node, assigned to $flagged, omit to use $element
+ * @cfg {jQuery} [$flagged] The flagged element. By default,
+ *  the flagged functionality is applied to the element created by the class ($element).
+ *  If a different element is specified, the flagged functionality will be applied to it instead.
  */
 OO.ui.FlaggedElement = function OoUiFlaggedElement( config ) {
        // Configuration initialization
@@ -5730,9 +5832,10 @@ OO.ui.FlaggedElement = function OoUiFlaggedElement( config ) {
 /**
  * Set the flagged element.
  *
- * If an element is already set, it will be cleaned up before setting up the new element.
+ * This method is used to retarget a flagged mixin so that its functionality applies to the specified element.
+ * If an element is already set, the method will remove the mixin’s effect on that element.
  *
- * @param {jQuery} $flagged Element to add flags to
+ * @param {jQuery} $flagged Element that should be flagged
  */
 OO.ui.FlaggedElement.prototype.setFlaggedElement = function ( $flagged ) {
        var classNames = Object.keys( this.flags ).map( function ( flag ) {
@@ -5747,10 +5850,10 @@ OO.ui.FlaggedElement.prototype.setFlaggedElement = function ( $flagged ) {
 };
 
 /**
- * Check if a flag is set.
+ * Check if the specified flag is set.
  *
  * @param {string} flag Name of flag
- * @return {boolean} Has flag
+ * @return {boolean} The flag is set
  */
 OO.ui.FlaggedElement.prototype.hasFlag = function ( flag ) {
        return flag in this.flags;
@@ -5797,8 +5900,9 @@ OO.ui.FlaggedElement.prototype.clearFlags = function () {
 /**
  * Add one or more flags.
  *
- * @param {string|string[]|Object.<string, boolean>} flags One or more flags to add, or an object
- *  keyed by flag name containing boolean set/remove instructions.
+ * @param {string|string[]|Object.<string, boolean>} flags A flag name, an array of flag names,
+ *  or an object keyed by flag name with a boolean value that indicates whether the flag should
+ *  be added (`true`) or removed (`false`).
  * @chainable
  * @fires flag
  */
@@ -5869,8 +5973,8 @@ OO.ui.FlaggedElement.prototype.setFlags = function ( flags ) {
  *     // TitledElement provides a 'title' attribute to the
  *     // ButtonWidget class
  *     var button = new OO.ui.ButtonWidget( {
- *         label : 'Button with Title',
- *         title : 'I am a button'
+ *         label: 'Button with Title',
+ *         title: 'I am a button'
  *     } );
  *     $( 'body' ).append( button.$element );
  *
@@ -5883,7 +5987,7 @@ OO.ui.FlaggedElement.prototype.setFlags = function ( flags ) {
  *  If this config is omitted, the title functionality is applied to $element, the
  *  element created by the class.
  * @cfg {string|Function} [title] The title text or a function that returns text. If
- *  this config is omitted, the value of the static `title` property is used.
+ *  this config is omitted, the value of the {@link #static-title static title} property is used.
  */
 OO.ui.TitledElement = function OoUiTitledElement( config ) {
        // Configuration initialization
@@ -5919,9 +6023,10 @@ OO.ui.TitledElement.static.title = null;
 /**
  * Set the titled element.
  *
- * If an element is already set, it will be cleaned up before setting up the new element.
+ * This method is used to retarget a titledElement mixin so that its functionality applies to the specified element.
+ * If an element is already set, the mixin’s effect on that element is removed before the new element is set up.
  *
- * @param {jQuery} $titled Element to set title on
+ * @param {jQuery} $titled Element that should use the 'titled' functionality
  */
 OO.ui.TitledElement.prototype.setTitledElement = function ( $titled ) {
        if ( this.$titled ) {
@@ -5937,7 +6042,7 @@ OO.ui.TitledElement.prototype.setTitledElement = function ( $titled ) {
 /**
  * Set title.
  *
- * @param {string|Function|null} title Title text, a function that returns text or null for no title
+ * @param {string|Function|null} title Title text, a function that returns text, or `null` for no title
  * @chainable
  */
 OO.ui.TitledElement.prototype.setTitle = function ( title ) {
@@ -7344,39 +7449,39 @@ OO.ui.MessageDialog.prototype.fitActions = function () {
  *
  *     @example
  *     // Example: Creating and opening a process dialog window.
- *     function ProcessDialog( config ) {
- *         ProcessDialog.super.call( this, config );
+ *     function MyProcessDialog( config ) {
+ *         MyProcessDialog.super.call( this, config );
  *     }
- *     OO.inheritClass( ProcessDialog, OO.ui.ProcessDialog );
+ *     OO.inheritClass( MyProcessDialog, OO.ui.ProcessDialog );
  *
- *     ProcessDialog.static.title = 'Process dialog';
- *     ProcessDialog.static.actions = [
+ *     MyProcessDialog.static.title = 'Process dialog';
+ *     MyProcessDialog.static.actions = [
  *         { action: 'save', label: 'Done', flags: 'primary' },
  *         { label: 'Cancel', flags: 'safe' }
  *     ];
  *
- *     ProcessDialog.prototype.initialize = function () {
- *         ProcessDialog.super.prototype.initialize.apply( this, arguments );
+ *     MyProcessDialog.prototype.initialize = function () {
+ *         MyProcessDialog.super.prototype.initialize.apply( this, arguments );
  *         this.content = new OO.ui.PanelLayout( { $: this.$, padded: true, expanded: false } );
- *         this.content.$element.append( '<p>This is a process dialog window. The header contains the title and two buttons: \'Cancel\' (a safe action) on the left and \'Done\' (a primary action)  on the right. </p>' );
+ *         this.content.$element.append( '<p>This is a process dialog window. The header contains the title and two buttons: \'Cancel\' (a safe action) on the left and \'Done\' (a primary action)  on the right.</p>' );
  *         this.$body.append( this.content.$element );
  *     };
- *     ProcessDialog.prototype.getActionProcess = function ( action ) {
+ *     MyProcessDialog.prototype.getActionProcess = function ( action ) {
  *         var dialog = this;
  *         if ( action ) {
  *             return new OO.ui.Process( function () {
- *             dialog.close( { action: action } );
- *         } );
- *     }
- *     return ProcessDialog.super.prototype.getActionProcess.call( this, action );
+ *                 dialog.close( { action: action } );
+ *             } );
+ *         }
+ *         return MyProcessDialog.super.prototype.getActionProcess.call( this, action );
  *     };
  *
  *     var windowManager = new OO.ui.WindowManager();
  *     $( 'body' ).append( windowManager.$element );
  *
- *     var processDialog = new ProcessDialog();
- *     windowManager.addWindows( [ processDialog ] );
- *     windowManager.openWindow( processDialog );
+ *     var dialog = new MyProcessDialog();
+ *     windowManager.addWindows( [ dialog ] );
+ *     windowManager.openWindow( dialog );
  *
  * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Process_Dialogs
  *
@@ -7482,6 +7587,19 @@ OO.ui.ProcessDialog.prototype.initialize = function () {
        this.$foot.append( this.$otherActions );
 };
 
+/**
+ * @inheritdoc
+ */
+OO.ui.ProcessDialog.prototype.getActionWidgets = function ( actions ) {
+       var i, len, widgets = [];
+       for ( i = 0, len = actions.length; i < len; i++ ) {
+               widgets.push(
+                       new OO.ui.ActionWidget( $.extend( { framed: true }, actions[ i ] ) )
+               );
+       }
+       return widgets;
+};
+
 /**
  * @inheritdoc
  */
@@ -7495,16 +7613,13 @@ OO.ui.ProcessDialog.prototype.attachActions = function () {
        others = this.actions.getOthers();
        if ( special.primary ) {
                this.$primaryActions.append( special.primary.$element );
-               special.primary.toggleFramed( true );
        }
        for ( i = 0, len = others.length; i < len; i++ ) {
                other = others[ i ];
                this.$otherActions.append( other.$element );
-               other.toggleFramed( true );
        }
        if ( special.safe ) {
                this.$safeActions.append( special.safe.$element );
-               special.safe.toggleFramed( true );
        }
 
        this.fitLabel();
@@ -7515,8 +7630,11 @@ OO.ui.ProcessDialog.prototype.attachActions = function () {
  * @inheritdoc
  */
 OO.ui.ProcessDialog.prototype.executeAction = function ( action ) {
+       var process = this;
        OO.ui.ProcessDialog.super.prototype.executeAction.call( this, action )
-               .fail( this.showErrors.bind( this ) );
+               .fail( function ( errors ) {
+                       process.showErrors( errors || [] );
+               } );
 };
 
 /**
@@ -9775,21 +9893,21 @@ OO.ui.ToggleWidget.prototype.setValue = function ( value ) {
  *     @example
  *     // Example: A ButtonGroupWidget with two buttons
  *     var button1 = new OO.ui.PopupButtonWidget( {
- *         label : 'Select a category',
- *         icon : 'menu',
- *         popup : {
+ *         label: 'Select a category',
+ *         icon: 'menu',
+ *         popup: {
  *             $content: $( '<p>List of categories...</p>' ),
  *             padded: true,
  *             align: 'left'
  *         }
  *     } );
  *     var button2 = new OO.ui.ButtonWidget( {
- *         label : 'Add item'
+ *         label: 'Add item'
  *     });
  *     var buttonGroup = new OO.ui.ButtonGroupWidget( {
  *         items: [button1, button2]
  *     } );
- *     $('body').append(buttonGroup.$element);
+ *     $( 'body' ).append( buttonGroup.$element );
  *
  * @class
  * @extends OO.ui.Widget
@@ -9832,9 +9950,9 @@ OO.mixinClass( OO.ui.ButtonGroupWidget, OO.ui.GroupElement );
  *     @example
  *     // A button widget
  *     var button = new OO.ui.ButtonWidget( {
- *         label : 'Button with Icon',
- *         icon : 'remove',
- *         iconTitle : 'Remove'
+ *         label: 'Button with Icon',
+ *         icon: 'remove',
+ *         iconTitle: 'Remove'
  *     } );
  *     $( 'body' ).append( button.$element );
  *
@@ -10353,7 +10471,7 @@ OO.ui.ToggleButtonWidget.prototype.setValue = function ( value ) {
  *
  *     @example
  *     // Example: A DropdownWidget with a menu that contains three options
- *     var dropDown=new OO.ui.DropdownWidget( {
+ *     var dropDown = new OO.ui.DropdownWidget( {
  *         label: 'Dropdown menu: Select a menu option',
  *         menu: {
  *             items: [
@@ -10373,7 +10491,7 @@ OO.ui.ToggleButtonWidget.prototype.setValue = function ( value ) {
  *         }
  *     } );
  *
- *     $('body').append(dropDown.$element);
+ *     $( 'body' ).append( dropDown.$element );
  *
  * For more information, please see the [OOjs UI documentation on MediaWiki] [1].
  *
@@ -10503,15 +10621,15 @@ OO.ui.DropdownWidget.prototype.onKeyPress = function ( e ) {
  *
  *     @example
  *     // An icon widget with a label
- *     var myIcon = new OO.ui.IconWidget({
+ *     var myIcon = new OO.ui.IconWidget( {
  *         icon: 'help',
  *         iconTitle: 'Help'
- *      });
+ *      } );
  *      // Create a label.
- *      var iconLabel = new OO.ui.LabelWidget({
+ *      var iconLabel = new OO.ui.LabelWidget( {
  *          label: 'Help'
- *      });
- *      $('body').append(myIcon.$element, iconLabel.$element);
+ *      } );
+ *      $( 'body' ).append( myIcon.$element, iconLabel.$element );
  *
  * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Icons,_Indicators,_and_Labels#Icons
  *
@@ -10557,12 +10675,12 @@ OO.ui.IconWidget.static.tagName = 'span';
  *     // Example of an indicator widget
  *     var indicator1 = new OO.ui.IndicatorWidget( {
  *         indicator: 'alert'
- *     });
+ *     } );
  *
  *     // Create a fieldset layout to add a label
- *     var fieldset = new OO.ui.FieldsetLayout( );
+ *     var fieldset = new OO.ui.FieldsetLayout();
  *     fieldset.addItems( [
- *         new OO.ui.FieldLayout( indicator1, {label: 'An alert indicator:'} )
+ *         new OO.ui.FieldLayout( indicator1, { label: 'An alert indicator:' } )
  *     ] );
  *     $( 'body' ).append( fieldset.$element );
  *
@@ -10942,13 +11060,13 @@ OO.ui.ButtonInputWidget.prototype.setValue = function ( value ) {
  *
  *     @example
  *     // An example of selected, unselected, and disabled checkbox inputs
- *     var checkbox1=new OO.ui.CheckboxInputWidget({
+ *     var checkbox1=new OO.ui.CheckboxInputWidget( {
  *          value: 'a',
  *          selected: true
- *     });
- *     var checkbox2=new OO.ui.CheckboxInputWidget({
+ *     } );
+ *     var checkbox2=new OO.ui.CheckboxInputWidget( {
  *         value: 'b'
- *     });
+ *     } );
  *     var checkbox3=new OO.ui.CheckboxInputWidget( {
  *         value:'c',
  *         disabled: true
@@ -10958,9 +11076,9 @@ OO.ui.ButtonInputWidget.prototype.setValue = function ( value ) {
  *         label: 'Checkboxes'
  *     } );
  *     fieldset.addItems( [
- *         new OO.ui.FieldLayout( checkbox1, {label : 'Selected checkbox', align : 'inline'}),
- *         new OO.ui.FieldLayout( checkbox2, {label : 'Unselected checkbox', align : 'inline'}),
- *         new OO.ui.FieldLayout( checkbox3, {label : 'Disabled checkbox', align : 'inline'}),
+ *         new OO.ui.FieldLayout( checkbox1, { label: 'Selected checkbox', align: 'inline' } ),
+ *         new OO.ui.FieldLayout( checkbox2, { label: 'Unselected checkbox', align: 'inline' } ),
+ *         new OO.ui.FieldLayout( checkbox3, { label: 'Disabled checkbox', align: 'inline' } ),
  *     ] );
  *     $( 'body' ).append( fieldset.$element );
  *
@@ -11051,7 +11169,7 @@ OO.ui.CheckboxInputWidget.prototype.isSelected = function () {
  *
  *     @example
  *     // Example: A DropdownInputWidget with three options
- *     var dropDown=new OO.ui.DropdownInputWidget( {
+ *     var dropDown = new OO.ui.DropdownInputWidget( {
  *         label: 'Dropdown menu: Select a menu option',
  *         options: [
  *             { data: 'a', label: 'First' } ,
@@ -11059,7 +11177,7 @@ OO.ui.CheckboxInputWidget.prototype.isSelected = function () {
  *             { data: 'c', label: 'Third' }
  *         ]
  *     } );
- *     $('body').append(dropDown.$element);
+ *     $( 'body' ).append( dropDown.$element );
  *
  * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Inputs
  *
@@ -11192,15 +11310,15 @@ OO.ui.DropdownInputWidget.prototype.blur = function () {
  *
  *     @example
  *     // An example of selected, unselected, and disabled radio inputs
- *     var radio1=new OO.ui.RadioInputWidget({
+ *     var radio1 = new OO.ui.RadioInputWidget( {
  *         value: 'a',
  *         selected: true
- *     });
- *     var radio2=new OO.ui.RadioInputWidget({
+ *     } );
+ *     var radio2 = new OO.ui.RadioInputWidget( {
  *         value: 'b'
- *     });
- *     var radio3=new OO.ui.RadioInputWidget( {
- *         value:'c',
+ *     } );
+ *     var radio3 = new OO.ui.RadioInputWidget( {
+ *         value: 'c',
  *         disabled: true
  *     } );
  *     // Create a fieldset layout with fields for each radio button.
@@ -11208,9 +11326,9 @@ OO.ui.DropdownInputWidget.prototype.blur = function () {
  *         label: 'Radio inputs'
  *     } );
  *     fieldset.addItems( [
- *         new OO.ui.FieldLayout( radio1, {label : 'Selected', align : 'inline'}),
- *         new OO.ui.FieldLayout( radio2, {label : 'Unselected', align : 'inline'}),
- *         new OO.ui.FieldLayout( radio3, {label : 'Disabled', align : 'inline'}),
+ *         new OO.ui.FieldLayout( radio1, { label: 'Selected', align: 'inline' } ),
+ *         new OO.ui.FieldLayout( radio2, { label: 'Unselected', align: 'inline' } ),
+ *         new OO.ui.FieldLayout( radio3, { label: 'Disabled', align: 'inline' } ),
  *     ] );
  *     $( 'body' ).append( fieldset.$element );
  *
@@ -11287,10 +11405,10 @@ OO.ui.RadioInputWidget.prototype.isSelected = function () {
  *
  *     @example
  *     // Example of a text input widget
- *     var textInput=new OO.ui.TextInputWidget( {
+ *     var textInput = new OO.ui.TextInputWidget( {
  *         value: 'Text input'
  *     } )
- *     $('body').append(textInput.$element);
+ *     $( 'body' ).append( textInput.$element );
  *
  * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Inputs
  *
@@ -11800,30 +11918,35 @@ OO.ui.TextInputWidget.prototype.positionLabel = function () {
  *
  *     @example
  *     // Example: A ComboBoxWidget.
- *     var comboBox=new OO.ui.ComboBoxWidget( {
+ *     var comboBox = new OO.ui.ComboBoxWidget( {
  *         label: 'ComboBoxWidget',
  *         input: { value: 'Option One' },
  *         menu: {
  *             items: [
  *                 new OO.ui.MenuOptionWidget( {
  *                     data: 'Option 1',
- *                     label: 'Option One' } ),
+ *                     label: 'Option One'
+ *                 } ),
  *                 new OO.ui.MenuOptionWidget( {
  *                     data: 'Option 2',
- *                     label: 'Option Two' } ),
+ *                     label: 'Option Two'
+ *                 } ),
  *                 new OO.ui.MenuOptionWidget( {
  *                     data: 'Option 3',
- *                     label: 'Option Three'} ),
+ *                     label: 'Option Three'
+ *                 } ),
  *                 new OO.ui.MenuOptionWidget( {
  *                     data: 'Option 4',
- *                     label: 'Option Four' } ),
+ *                     label: 'Option Four'
+ *                 } ),
  *                 new OO.ui.MenuOptionWidget( {
  *                     data: 'Option 5',
- *                     label: 'Option Five' } )
+ *                     label: 'Option Five'
+ *                 } )
  *             ]
  *         }
  *     } );
- *     $('body').append(comboBox.$element);
+ *     $( 'body' ).append( comboBox.$element );
  *
  * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options#Menu_selects_and_options
  *
@@ -12029,14 +12152,14 @@ OO.ui.ComboBoxWidget.prototype.setDisabled = function ( disabled ) {
  *
  *     @example
  *     // Examples of LabelWidgets
- *     var label1 = new OO.ui.LabelWidget({
+ *     var label1 = new OO.ui.LabelWidget( {
  *         label: 'plaintext label'
- *     });
- *     var label2 = new OO.ui.LabelWidget({
- *         label: $( '<a href="default.html">jQuery label</a>'  )
- *     });
+ *     } );
+ *     var label2 = new OO.ui.LabelWidget( {
+ *         label: $( '<a href="default.html">jQuery label</a>' )
+ *     } );
  *     // Create a fieldset layout with fields for each example
- *     var fieldset = new OO.ui.FieldsetLayout( );
+ *     var fieldset = new OO.ui.FieldsetLayout();
  *     fieldset.addItems( [
  *         new OO.ui.FieldLayout( label1 ),
  *         new OO.ui.FieldLayout( label2 )
@@ -12286,7 +12409,7 @@ OO.ui.OptionWidget.prototype.setPressed = function ( state ) {
  *
  *     @example
  *     // Decorated options in a select widget
- *     var select=new OO.ui.SelectWidget( {
+ *     var select = new OO.ui.SelectWidget( {
  *         items: [
  *             new OO.ui.DecoratedOptionWidget( {
  *                 data: 'a',
@@ -12300,7 +12423,7 @@ OO.ui.OptionWidget.prototype.setPressed = function ( state ) {
  *             } )
  *         ]
  *     } );
- *     $('body').append(select.$element);
+ *     $( 'body' ).append( select.$element );
  *
  * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options
  *
@@ -12656,15 +12779,15 @@ OO.ui.OutlineOptionWidget.prototype.setLevel = function ( level ) {
  *
  *     @example
  *     // A popup widget.
- *     var popup=new OO.ui.PopupWidget({
+ *     var popup = new OO.ui.PopupWidget( {
  *         $content: $( '<p>Hi there!</p>' ),
  *         padded: true,
  *         width: 300
  *     } );
  *
- *     $('body').append(popup.$element);
+ *     $( 'body' ).append( popup.$element );
  *     // To display the popup, toggle the visibility to 'true'.
- *     popup.toggle(true);
+ *     popup.toggle( true );
  *
  * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Popups
  *
@@ -13021,18 +13144,16 @@ OO.ui.PopupWidget.prototype.updateDimensions = function ( transition ) {
  *
  *     @example
  *     // Examples of determinate and indeterminate progress bars.
- *     var progressBar1=new OO.ui.ProgressBarWidget( {
+ *     var progressBar1 = new OO.ui.ProgressBarWidget( {
  *         progress: 33
  *     } );
+ *     var progressBar2 = new OO.ui.ProgressBarWidget();
  *
- *     var progressBar2=new OO.ui.ProgressBarWidget( {
- *         progress: false
- *     } );
  *     // Create a FieldsetLayout to layout progress bars
  *     var fieldset = new OO.ui.FieldsetLayout;
  *     fieldset.addItems( [
- *        new OO.ui.FieldLayout( progressBar1, {label : 'Determinate', align : 'top'}),
- *        new OO.ui.FieldLayout( progressBar2, {label : 'Indeterminate', align : 'top'})
+ *        new OO.ui.FieldLayout( progressBar1, {label: 'Determinate', align: 'top'}),
+ *        new OO.ui.FieldLayout( progressBar2, {label: 'Indeterminate', align: 'top'})
  *     ] );
  *     $( 'body' ).append( fieldset.$element );
  *
@@ -13271,7 +13392,7 @@ OO.ui.SearchWidget.prototype.getResults = function () {
  *
  *     @example
  *     // Example of a select widget with three options
- *     var select=new OO.ui.SelectWidget( {
+ *     var select = new OO.ui.SelectWidget( {
  *         items: [
  *             new OO.ui.OptionWidget( {
  *                 data: 'a',
@@ -13284,10 +13405,10 @@ OO.ui.SearchWidget.prototype.getResults = function () {
  *             new OO.ui.OptionWidget( {
  *                 data: 'c',
  *                 label: 'Option Three',
- *             } ),
+ *             } )
  *         ]
  *     } );
- *     $('body').append(select.$element);
+ *     $( 'body' ).append( select.$element );
  *
  * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options
  *
@@ -13890,25 +14011,25 @@ OO.ui.SelectWidget.prototype.clearItems = function () {
  *     var option1 = new OO.ui.ButtonOptionWidget( {
  *         data: 1,
  *         label: 'Option 1',
- *         title:'Button option 1'
+ *         title: 'Button option 1'
  *     } );
  *
  *     var option2 = new OO.ui.ButtonOptionWidget( {
  *         data: 2,
  *         label: 'Option 2',
- *         title:'Button option 2'
+ *         title: 'Button option 2'
  *     } );
  *
  *     var option3 = new OO.ui.ButtonOptionWidget( {
  *         data: 3,
  *         label: 'Option 3',
- *         title:'Button option 3'
+ *         title: 'Button option 3'
  *     } );
  *
  *     var buttonSelect=new OO.ui.ButtonSelectWidget( {
- *         items: [option1, option2, option3]
+ *         items: [ option1, option2, option3 ]
  *     } );
- *     $('body').append(buttonSelect.$element);
+ *     $( 'body' ).append( buttonSelect.$element );
  *
  * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options
  *
@@ -13960,13 +14081,13 @@ OO.mixinClass( OO.ui.ButtonSelectWidget, OO.ui.TabIndexedElement );
  *     } );
  *
  *     var radioSelect=new OO.ui.RadioSelectWidget( {
- *         items: [option1, option2]
+ *         items: [ option1, option2 ]
  *      } );
  *
  *     // Select 'option 1' using the RadioSelectWidget's selectItem() method.
  *     radioSelect.selectItem( option1 );
  *
- *     $('body').append(radioSelect.$element);
+ *     $( 'body' ).append( radioSelect.$element );
  *
  * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options
 
@@ -14002,9 +14123,10 @@ OO.mixinClass( OO.ui.RadioSelectWidget, OO.ui.TabIndexedElement );
 
 /**
  * MenuSelectWidget is a {@link OO.ui.SelectWidget select widget} that contains options and
- * is used together with OO.ui.MenuOptionWidget. See {@link OO.ui.DropdownWidget DropdownWidget} and
- * {@link OO.ui.ComboBoxWidget ComboBoxWidget} for examples of interfaces that contain menus.
- * MenuSelectWidgets themselves are not designed to be instantiated directly, rather subclassed
+ * is used together with OO.ui.MenuOptionWidget. It is designed be used as part of another widget.
+ * See {@link OO.ui.DropdownWidget DropdownWidget}, {@link OO.ui.ComboBoxWidget ComboBoxWidget},
+ * and {@link OO.ui.LookupElement LookupElement} for examples of widgets that contain menus.
+ * MenuSelectWidgets themselves are not instantiated directly, rather subclassed
  * and customized to be opened, closed, and displayed as needed.
  *
  * By default, menus are clipped to the visible viewport and are not visible when a user presses the
@@ -14026,9 +14148,12 @@ OO.mixinClass( OO.ui.RadioSelectWidget, OO.ui.TabIndexedElement );
  *
  * @constructor
  * @param {Object} [config] Configuration options
- * @cfg {OO.ui.TextInputWidget} [input] Input to bind keyboard handlers to
- * @cfg {OO.ui.Widget} [widget] Widget to bind mouse handlers to
- * @cfg {boolean} [autoHide=true] Hide the menu when the mouse is pressed outside the menu
+ * @cfg {OO.ui.TextInputWidget} [input] Text input used to implement option highlighting for menu items that match
+ *  the text the user types. This config is used by {@link OO.ui.ComboBoxWidget ComboBoxWidget}
+ *  and {@link OO.ui.LookupElement LookupElement}
+ * @cfg {OO.ui.Widget} [widget] Widget associated with the menu’s active state. If the user clicks the mouse
+ *  anywhere on the page outside of this widget, the menu is hidden.
+ * @cfg {boolean} [autoHide=true] Hide the menu when the mouse is pressed outside the menu.
  */
 OO.ui.MenuSelectWidget = function OoUiMenuSelectWidget( config ) {
        // Configuration initialization
@@ -14140,8 +14265,10 @@ OO.ui.MenuSelectWidget.prototype.unbindKeyDownListener = function () {
 /**
  * Choose an item.
  *
- * This will close the menu, unlike #selectItem which only changes selection.
+ * When a user chooses an item, the menu is closed.
  *
+ * Note that ‘choose’ should never be modified programmatically. A user can choose an option with the keyboard
+ * or mouse and it becomes selected. To select an item programmatically, use the #selectItem method.
  * @param {OO.ui.OptionWidget} item Item to choose
  * @chainable
  */
@@ -14395,10 +14522,8 @@ OO.mixinClass( OO.ui.OutlineSelectWidget, OO.ui.TabIndexedElement );
  *
  *     @example
  *     // Toggle switches in the 'off' and 'on' position.
- *     var toggleSwitch1 = new OO.ui.ToggleSwitchWidget({
- *         value: false
- *      } );
- *     var toggleSwitch2 = new OO.ui.ToggleSwitchWidget({
+ *     var toggleSwitch1 = new OO.ui.ToggleSwitchWidget();
+ *     var toggleSwitch2 = new OO.ui.ToggleSwitchWidget( {
  *         value: true
  *     } );
  *
@@ -14407,8 +14532,8 @@ OO.mixinClass( OO.ui.OutlineSelectWidget, OO.ui.TabIndexedElement );
  *        label: 'Toggle switches'
  *     } );
  *     fieldset.addItems( [
- *         new OO.ui.FieldLayout( toggleSwitch1, {label : 'Off', align : 'top'}),
- *         new OO.ui.FieldLayout( toggleSwitch2, {label : 'On', align : 'top'})
+ *         new OO.ui.FieldLayout( toggleSwitch1, { label: 'Off', align: 'top' } ),
+ *         new OO.ui.FieldLayout( toggleSwitch2, { label: 'On', align: 'top' } )
  *     ] );
  *     $( 'body' ).append( fieldset.$element );
  *
index cf5a616..18dc564 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs v1.1.5 optimised for jQuery
+ * OOjs v1.1.6 optimised for jQuery
  * https://www.mediawiki.org/wiki/OOjs
  *
  * Copyright 2011-2015 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2015-02-26T01:51:06Z
+ * Date: 2015-03-19T00:42:55Z
  */
 ( function ( global ) {
 
@@ -435,6 +435,21 @@ oo.getHash.keySortReplacer = function ( key, val ) {
        }
 };
 
+/**
+ * Get the unique values of an array, removing duplicates
+ *
+ * @param {Array} arr Array
+ * @return {Array} Unique values in array
+ */
+oo.unique = function ( arr ) {
+       return arr.reduce( function ( result, current ) {
+               if ( result.indexOf( current ) === -1 ) {
+                       result.push( current );
+               }
+               return result;
+       }, [] );
+};
+
 /**
  * Compute the union (duplicate-free merge) of a set of arrays.
  *
diff --git a/resources/src/mediawiki.api/mediawiki.api.options.js b/resources/src/mediawiki.api/mediawiki.api.options.js
new file mode 100644 (file)
index 0000000..b839fbd
--- /dev/null
@@ -0,0 +1,89 @@
+/**
+ * @class mw.Api.plugin.options
+ */
+( function ( mw, $ ) {
+
+       $.extend( mw.Api.prototype, {
+
+               /**
+                * Asynchronously save the value of a single user option using the API. See #saveOptions.
+                *
+                * @param {string} name
+                * @param {string|null} value
+                * @return {jQuery.Promise}
+                */
+               saveOption: function ( name, value ) {
+                       var param = {};
+                       param[name] = value;
+                       return this.saveOptions( param );
+               },
+
+               /**
+                * Asynchronously save the values of user options using the API.
+                *
+                * If a value of `null` is provided, the given option will be reset to the default value.
+                *
+                * Any warnings returned by the API, including warnings about invalid option names or values,
+                * are ignored. However, do not rely on this behavior.
+                *
+                * If necessary, the options will be saved using several parallel API requests. Only one promise
+                * is always returned that will be resolved when all requests complete.
+                *
+                * @param {Object} options Options as a `{ name: value, … }` object
+                * @return {jQuery.Promise}
+                */
+               saveOptions: function ( options ) {
+                       var name, value, bundleable,
+                               grouped = [],
+                               deferreds = [];
+
+                       for ( name in options ) {
+                               value = options[name] === null ? null : String( options[name] );
+
+                               // Can we bundle this option, or does it need a separate request?
+                               bundleable =
+                                       ( value === null || value.indexOf( '|' ) === -1 ) &&
+                                       ( name.indexOf( '|' ) === -1 && name.indexOf( '=' ) === -1 );
+
+                               if ( bundleable ) {
+                                       if ( value !== null ) {
+                                               grouped.push( name + '=' + value );
+                                       } else {
+                                               // Omitting value resets the option
+                                               grouped.push( name );
+                                       }
+                               } else {
+                                       if ( value !== null ) {
+                                               deferreds.push( this.postWithToken( 'options', {
+                                                       action: 'options',
+                                                       optionname: name,
+                                                       optionvalue: value
+                                               } ) );
+                                       } else {
+                                               // Omitting value resets the option
+                                               deferreds.push( this.postWithToken( 'options', {
+                                                       action: 'options',
+                                                       optionname: name
+                                               } ) );
+                                       }
+                               }
+                       }
+
+                       if ( grouped.length ) {
+                               deferreds.push( this.postWithToken( 'options', {
+                                       action: 'options',
+                                       change: grouped.join( '|' )
+                               } ) );
+                       }
+
+                       return $.when.apply( $, deferreds );
+               }
+
+       } );
+
+       /**
+        * @class mw.Api
+        * @mixins mw.Api.plugin.options
+        */
+
+}( mediaWiki, jQuery ) );
index 8579e05..aca335e 100644 (file)
@@ -12,7 +12,7 @@
                function updateBlockOptions( instant ) {
                        var blocktarget = $.trim( $blockTarget.val() ),
                                isEmpty = blocktarget === '',
-                               isIp = mw.util.isIPv4Address( blocktarget, true ) || mw.util.isIPv6Address( blocktarget, true ),
+                               isIp = mw.util.isIPAddress( blocktarget, true ),
                                isIpRange = isIp && blocktarget.match( /\/\d+$/ );
 
                        if ( isIp && !isEmpty ) {
index c92db16..16fdf38 100644 (file)
@@ -5,3 +5,11 @@
 .mw-changeslist-line-watched .mw-title {
        font-weight: bold;
 }
+
+/*
+ * Titles, including username links, are especially prone for getting jumbled up
+ * with other titles, usernames, etc. in mixed RTL-LTR environment.
+ */
+.mw-changeslist .mw-title {
+       unicode-bidi: embed;
+}
index 1992cce..2f76131 100644 (file)
@@ -128,11 +128,3 @@ input.mw-ui-input-large {
        font-weight: bold;
        line-height: 1.25em;
 }
-
-// Tablet and desktop specific styling tweaks.
-@media all and (min-width: 768px) {
-       // Make inline elements take up a sensible amount of the screen on wider devices.
-       .mw-ui-input-inline {
-               min-width: 320px;
-       }
-}
diff --git a/resources/src/mediawiki/mediawiki.filewarning.js b/resources/src/mediawiki/mediawiki.filewarning.js
new file mode 100644 (file)
index 0000000..882affe
--- /dev/null
@@ -0,0 +1,68 @@
+/*!
+ * mediawiki.filewarning
+ *
+ * @author Mark Holmquist, 2015
+ * @since 1.25
+ */
+/*global OO*/
+( function ( mw, $, oo ) {
+       var warningConfig = mw.config.get( 'wgFileWarning' ),
+               warningMessages = warningConfig.messages,
+               warningLink = warningConfig.link,
+               $origMimetype = $( '.fullMedia .fileInfo .mime-type' ),
+               $mimetype = $origMimetype.clone(),
+               $header = $( '<h3>' )
+                       .addClass( 'mediawiki-filewarning-header empty' ),
+               $main = $( '<p>' )
+                       .addClass( 'mediawiki-filewarning-main empty' ),
+               $info = $( '<a>' )
+                       .addClass( 'mediawiki-filewarning-info empty' ),
+               $footer = $( '<p>' )
+                       .addClass( 'mediawiki-filewarning-footer empty' ),
+               dialog = new oo.ui.PopupButtonWidget( {
+                       classes: [ 'mediawiki-filewarning-anchor' ],
+                       label: $mimetype,
+                       flags: [ 'warning' ],
+                       icon: 'alert',
+                       framed: false,
+                       popup: {
+                               classes: [ 'mediawiki-filewarning' ],
+                               padded: true,
+                               width: 400,
+                               $content: $header.add( $main ).add( $info ).add( $footer )
+                       }
+               } );
+
+       function loadMessage( $target, message ) {
+               if ( message ) {
+                       $target.removeClass( 'empty' )
+                               .text( mw.message( message ).text() );
+               }
+       }
+
+       // The main message must be populated for the dialog to show.
+       if ( warningConfig && warningConfig.messages && warningConfig.messages.main ) {
+               $mimetype.addClass( 'has-warning' );
+
+               $origMimetype.replaceWith( dialog.$element );
+
+               if ( warningMessages ) {
+                       loadMessage( $main, warningMessages.main );
+                       loadMessage( $header, warningMessages.header );
+                       loadMessage( $footer, warningMessages.footer );
+
+                       if ( warningLink ) {
+                               loadMessage( $info, warningMessages.info );
+                               $info.attr( 'href', warningLink );
+                       }
+               }
+
+               // Make OOUI open the dialog, it won't appear until the user
+               // hovers over the warning.
+               dialog.getPopup().toggle( true );
+
+               // Override toggle handler because we don't need it for this popup
+               // object at all. Sort of nasty, but it gets the job done.
+               dialog.getPopup().toggle = $.noop;
+       }
+}( mediaWiki, jQuery, OO ) );
diff --git a/resources/src/mediawiki/mediawiki.filewarning.less b/resources/src/mediawiki/mediawiki.filewarning.less
new file mode 100644 (file)
index 0000000..489ac42
--- /dev/null
@@ -0,0 +1,29 @@
+@import "mediawiki.ui/variables"
+
+.mediawiki-filewarning {
+       display: none;
+
+       .mediawiki-filewarning-header {
+               padding: 0;
+               font-weight: 600;
+       }
+
+       .mediawiki-filewarning-footer {
+               color: #888888;
+       }
+
+       .empty {
+               display: none;
+       }
+
+       .mediawiki-filewarning-anchor:hover & {
+               display: block;
+       }
+}
+
+.mime-type {
+       &.has-warning {
+               font-weight: bold;
+               color: @colorMediumSevere;
+       }
+}
index 2c3c90f..6723e5f 100644 (file)
 
                        return address.search( new RegExp( '^' + RE_IPV6_ADD + block + '$' ) ) !== -1
                                && address.search( /::/ ) !== -1 && address.search( /::.*::/ ) === -1;
+               },
+
+               /**
+                * Check whether a string is an IP address
+                *
+                * @since 1.25
+                * @param {string} address String to check
+                * @param {boolean} allowBlock True if a block of IPs should be allowed
+                * @return {boolean}
+                */
+               isIPAddress: function ( address, allowBlock ) {
+                       return util.isIPv4Address( address, allowBlock ) ||
+                               util.isIPv6Address( address, allowBlock );
                }
        };
 
index 79507ca..406a67e 100644 (file)
@@ -14,5 +14,5 @@ Given(/^I go to Create account page at (.+)$/) do |path|
 end
 
 Then(/^form has Create account button$/) do
-  on(CreateAccountPage).create_account_element.should exist
+  expect(on(CreateAccountPage).create_account_element).to exist
 end
index 6197b6b..065ee26 100644 (file)
@@ -18,9 +18,9 @@ When(/^I click the Link Target link$/) do
 end
 
 Then(/^I should be on the Link Target Test Page$/) do
-  browser.url.should match(/Link_Target_Test_Page/)
+  expect(@browser.url).to match /Link_Target_Test_Page/
 end
 
 Then(/^the page content should contain "(.*?)"$/) do |content|
-  on(ZtargetPage).page_content.should match content
+  expect(on(ZtargetPage).page_content).to match content
 end
index 713bb39..0e0aeb1 100644 (file)
@@ -19,5 +19,5 @@ When(/^I save the edit$/) do
 end
 
 Then(/^the edited page content should contain "(.*?)"$/) do |content|
-  on(MainPage).page_content.should match(content + @random_string)
+  expect(on(MainPage).page_content).to match(content + @random_string)
 end
index 6f017c1..a80ca50 100644 (file)
@@ -14,5 +14,5 @@ Given(/^I am at file that does not exist$/) do
 end
 
 Then(/^page should show that no such file exists$/) do
-  on(FileDoesNotExistPage).file_does_not_exist_message_element.should be_visible
+  expect(on(FileDoesNotExistPage).file_does_not_exist_message_element).to be_visible
 end
index f528eba..1f76e55 100644 (file)
@@ -30,36 +30,36 @@ When(/^I log in without entering password$/) do
 end
 
 Then(/^error box should be visible$/) do
-  on(LoginErrorPage).error_box_element.should be_visible
+  expect(on(LoginErrorPage).error_box_element).to be_visible
 end
 
 Then(/^error box should not be visible$/) do
-  on(LoginErrorPage).error_box_element.should_not be_visible
+  expect(on(LoginErrorPage).error_box_element).not_to be_visible
 end
 
 Then(/^feedback should be (.+)$/) do |feedback|
   on(LoginPage) do |page|
     page.feedback_element.when_present.click
-    page.feedback.should match Regexp.escape(feedback)
+    expect(page.feedback).to match Regexp.escape(feedback)
   end
 end
 
 Then(/^Log in element should be there$/) do
-  on(LoginPage).login_element.should exist
+  expect(on(LoginPage).login_element).to exist
 end
 
 Then(/^main page should open$/) do
-  browser.url.should == on(MainPage).class.url
+  expect(@browser.url).to eq on(MainPage).class.url
 end
 
 Then(/^Password element should be there$/) do
-  on(LoginPage).password_element.should exist
+  expect(on(LoginPage).password_element).to exist
 end
 
 Then(/^there should be a link to (.+)$/) do |text|
-  on(LoginPage).username_displayed_element.when_present.text.should == text
+  expect(on(LoginPage).username_displayed_element.when_present.text).to eq text
 end
 
 Then(/^Username element should be there$/) do
-  on(LoginPage).username_element.should exist
+  expect(on(LoginPage).username_element).to exist
 end
index c76fd2b..7f588c0 100644 (file)
@@ -3,45 +3,45 @@ Given(/^I open the main wiki URL$/) do
 end
 
 Then(/^I should see a link for View History$/) do
-  on(MainPage).view_history_link_element.should be_visible
+  expect(on(MainPage).view_history_link_element).to be_visible
 end
 
 Then(/^I should see a link for Edit$/) do
-  on(MainPage).edit_link_element.should be_visible
+  expect(on(MainPage).edit_link_element).to be_visible
 end
 
 Then(/^I should see a link for Recent changes$/) do
-  on(MainPage).recent_changes_link_element.should be_visible
+  expect(on(MainPage).recent_changes_link_element).to be_visible
 end
 
 Then(/^I should see a link for Random page$/) do
-  on(MainPage).random_page_link_element.should be_visible
+  expect(on(MainPage).random_page_link_element).to be_visible
 end
 
 Then(/^I should see a link for Help$/) do
-  on(MainPage).help_link_element.should be_visible
+  expect(on(MainPage).help_link_element).to be_visible
 end
 
 Then(/^I should see a link for What links here$/) do
-  on(MainPage).what_links_here_link_element.should be_visible
+  expect(on(MainPage).what_links_here_link_element).to be_visible
 end
 
 Then(/^I should see a link for Related changes$/) do
-  on(MainPage).related_changes_link_element.should be_visible
+  expect(on(MainPage).related_changes_link_element).to be_visible
 end
 
 Then(/^I should see a link for Special pages$/) do
-  on(MainPage).special_pages_link_element.should be_visible
+  expect(on(MainPage).special_pages_link_element).to be_visible
 end
 
 Then(/^I should see a link for Printable version$/) do
-  on(MainPage).printable_version_link_element.should be_visible
+  expect(on(MainPage).printable_version_link_element).to be_visible
 end
 
 Then(/^I should see a link for Permanent link$/) do
-  on(MainPage).permanent_link_link_element.should be_visible
+  expect(on(MainPage).permanent_link_link_element).to be_visible
 end
 
 Then(/^I should see a link for Page information$/) do
-  on(MainPage).page_information_link_element.should be_visible
+  expect(on(MainPage).page_information_link_element).to be_visible
 end
index 44f87de..133eec3 100644 (file)
@@ -18,66 +18,66 @@ When(/^I navigate to Preferences$/) do
 end
 
 Then(/^I can click Save$/) do
-  on(PreferencesPage).save_button_element.should exist
+  expect(on(PreferencesPage).save_button_element).to exist
 end
 
 Then(/^I can restore default settings$/) do
-  on(PreferencesAppearancePage).restore_default_link_element.should exist
+  expect(on(PreferencesAppearancePage).restore_default_link_element).to exist
 end
 
 Then(/^I can see local time$/) do
-  on(PreferencesAppearancePage).local_time_span_element.should exist
+  expect(on(PreferencesAppearancePage).local_time_span_element).to exist
 end
 
 Then(/^I can see time offset section$/) do
-  on(PreferencesAppearancePage).time_offset_table_element.should be_visible
+  expect(on(PreferencesAppearancePage).time_offset_table_element).to be_visible
 end
 
 Then(/^I can select date format$/) do
   on(PreferencesAppearancePage) do |page|
-    page.no_preference_radio_element.should exist
-    page.mo_day_year_radio_element.should exist
-    page.day_mo_year_radio_element.should exist
-    page.year_mo_day_radio_element.should exist
-    page.iso_8601_radio_element.should exist
+    expect(page.no_preference_radio_element).to exist
+    expect(page.mo_day_year_radio_element).to exist
+    expect(page.day_mo_year_radio_element).to exist
+    expect(page.year_mo_day_radio_element).to exist
+    expect(page.iso_8601_radio_element).to exist
   end
 end
 
 Then(/^I can select image size$/) do
-  on(PreferencesAppearancePage).size_select_element.should exist
+  expect(on(PreferencesAppearancePage).size_select_element).to exist
 end
 
 Then(/^I can select my time zone$/) do
   on(PreferencesAppearancePage) do |page|
-    page.time_offset_select_element.should exist
-    page.other_offset_element.should exist
+    expect(page.time_offset_select_element).to exist
+    expect(page.other_offset_element).to exist
   end
 end
 
 Then(/^I can select skins$/) do
   on(PreferencesAppearancePage) do |page|
-    page.cologne_blue_element.should exist
-    page.modern_element.should exist
-    page.monobook_element.should exist
-    page.vector_element.should exist
+    expect(page.cologne_blue_element).to exist
+    expect(page.modern_element).to exist
+    expect(page.monobook_element).to exist
+    expect(page.vector_element).to exist
   end
 end
 
 Then(/^I can select Threshold for stub link$/) do
-  on(PreferencesAppearancePage).threshold_select_element.should exist
+  expect(on(PreferencesAppearancePage).threshold_select_element).to exist
 end
 
 Then(/^I can select thumbnail size$/) do
-  on(PreferencesAppearancePage).thumb_select_element.should exist
+  expect(on(PreferencesAppearancePage).thumb_select_element).to exist
 end
 
 Then(/^I can select underline preferences$/) do
-  on(PreferencesAppearancePage).underline_select_element.should exist
+  expect(on(PreferencesAppearancePage).underline_select_element).to exist
 end
 
 Then(/^I have advanced options checkboxes$/) do
   on(PreferencesAppearancePage) do |page|
-    page.hidden_categories_check_element.should exist
-    page.auto_number_check_element.should exist
+    expect(page.hidden_categories_check_element).to exist
+    expect(page.auto_number_check_element).to exist
   end
 end
index ad29a74..0a98e88 100644 (file)
@@ -14,41 +14,41 @@ When(/^I click Editing$/) do
 end
 
 Then(/^I can select edit area font style$/) do
-  on(PreferencesEditingPage).edit_area_font_style_select_element.when_present.should exist
+  expect(on(PreferencesEditingPage).edit_area_font_style_select_element.when_present).to exist
 end
 
 Then(/^I can select live preview$/) do
-  on(PreferencesEditingPage).live_preview_check_element.when_present.should exist
+  expect(on(PreferencesEditingPage).live_preview_check_element.when_present).to exist
 end
 
 Then(/^I can select section editing by double clicking$/) do
-  on(PreferencesEditingPage).edit_section_double_click_check_element.when_present.should exist
+  expect(on(PreferencesEditingPage).edit_section_double_click_check_element.when_present).to exist
 end
 
 Then(/^I can select section editing by right clicking$/) do
-  on(PreferencesEditingPage).edit_section_right_click_check_element.when_present.should exist
+  expect(on(PreferencesEditingPage).edit_section_right_click_check_element.when_present).to exist
 end
 
 Then(/^I can select section editing via edit links$/) do
-  on(PreferencesEditingPage).edit_section_edit_link_element.when_present.should exist
+  expect(on(PreferencesEditingPage).edit_section_edit_link_element.when_present).to exist
 end
 
 Then(/^I can select show edit toolbar$/) do
-  on(PreferencesEditingPage).show_edit_toolbar_check_element.when_present.should exist
+  expect(on(PreferencesEditingPage).show_edit_toolbar_check_element.when_present).to exist
 end
 
 Then(/^I can select show preview before edit box$/) do
-  on(PreferencesEditingPage).preview_on_top_check_element.when_present.should exist
+  expect(on(PreferencesEditingPage).preview_on_top_check_element.when_present).to exist
 end
 
 Then(/^I can select show preview on first edit$/) do
-  on(PreferencesEditingPage).preview_on_first_check_element.when_present.should exist
+  expect(on(PreferencesEditingPage).preview_on_first_check_element.when_present).to exist
 end
 
 Then(/^I can select to prompt me when entering a blank edit summary$/) do
-  on(PreferencesEditingPage).forced_edit_summary_check_element.when_present.should exist
+  expect(on(PreferencesEditingPage).forced_edit_summary_check_element.when_present).to exist
 end
 
 Then(/^I can select to warn me when I leave an edit page with unsaved changes$/) do
-  on(PreferencesEditingPage).unsaved_changes_check_element.when_present.should exist
+  expect(on(PreferencesEditingPage).unsaved_changes_check_element.when_present).to exist
 end
index 6ecd04f..9c65db8 100644 (file)
@@ -15,28 +15,28 @@ end
 
 Then(/^I can change my gender$/) do
   on(PreferencesUserProfilePage) do |page|
-    page.gender_undefined_radio_element.should exist
-    page.gender_male_radio_element.should exist
-    page.gender_female_radio_element.should exist
+    expect(page.gender_undefined_radio_element).to exist
+    expect(page.gender_male_radio_element).to exist
+    expect(page.gender_female_radio_element).to exist
   end
 end
 
 Then(/^I can change my language$/) do
-  on(PreferencesUserProfilePage).lang_select_element.should exist
+  expect(on(PreferencesUserProfilePage).lang_select_element).to exist
 end
 
 Then(/^I can change my signature$/) do
-  on(PreferencesUserProfilePage).signature_field_element.should exist
+  expect(on(PreferencesUserProfilePage).signature_field_element).to exist
 end
 
 Then(/^I can see my Basic informations$/) do
-  on(PreferencesUserProfilePage).basic_info_table_element.should exist
+  expect(on(PreferencesUserProfilePage).basic_info_table_element).to exist
 end
 
 Then(/^I can see my email$/) do
-  on(PreferencesUserProfilePage).email_table_element.should exist
+  expect(on(PreferencesUserProfilePage).email_table_element).to exist
 end
 
 Then(/^I can see my signature$/) do
-  on(PreferencesUserProfilePage).signature_table_element.should exist
+  expect(on(PreferencesUserProfilePage).signature_table_element).to exist
 end
index 6bb7e83..d9b9381 100644 (file)
@@ -3,5 +3,5 @@ When(/^I click View History$/) do
 end
 
 Then(/^I should see a link to a previous version of the page$/) do
-  on(ViewHistoryPage).old_version_link_element.should be_visible
+  expect(on(ViewHistoryPage).old_version_link_element).to be_visible
 end
diff --git a/tests/phpunit/includes/site/CachingSiteStoreTest.php b/tests/phpunit/includes/site/CachingSiteStoreTest.php
new file mode 100644 (file)
index 0000000..8159b28
--- /dev/null
@@ -0,0 +1,162 @@
+<?php
+
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @since 1.25
+ *
+ * @ingroup Site
+ * @ingroup Test
+ *
+ * @group Site
+ * @group Database
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class CachingSiteStoreTest extends MediaWikiTestCase {
+
+       /**
+        * @covers CachingSiteStore::getSites
+        */
+       public function testGetSites() {
+               $testSites = TestSites::getSites();
+
+               $store = new CachingSiteStore(
+                       $this->getHashSiteStore( $testSites ),
+                       wfGetMainCache()
+               );
+
+               $sites = $store->getSites();
+
+               $this->assertInstanceOf( 'SiteList', $sites );
+
+               /**
+                * @var Site $site
+                */
+               foreach ( $sites as $site ) {
+                       $this->assertInstanceOf( 'Site', $site );
+               }
+
+               foreach ( $testSites as $site ) {
+                       if ( $site->getGlobalId() !== null ) {
+                               $this->assertTrue( $sites->hasSite( $site->getGlobalId() ) );
+                       }
+               }
+       }
+
+       /**
+        * @covers CachingSiteStore::saveSites
+        */
+       public function testSaveSites() {
+               $store = new CachingSiteStore( new HashSiteStore(), wfGetMainCache() );
+
+               $sites = array();
+
+               $site = new Site();
+               $site->setGlobalId( 'ertrywuutr' );
+               $site->setLanguageCode( 'en' );
+               $sites[] = $site;
+
+               $site = new MediaWikiSite();
+               $site->setGlobalId( 'sdfhxujgkfpth' );
+               $site->setLanguageCode( 'nl' );
+               $sites[] = $site;
+
+               $this->assertTrue( $store->saveSites( $sites ) );
+
+               $site = $store->getSite( 'ertrywuutr' );
+               $this->assertInstanceOf( 'Site', $site );
+               $this->assertEquals( 'en', $site->getLanguageCode() );
+
+               $site = $store->getSite( 'sdfhxujgkfpth' );
+               $this->assertInstanceOf( 'Site', $site );
+               $this->assertEquals( 'nl', $site->getLanguageCode() );
+       }
+
+       /**
+        * @covers CachingSiteStore::reset
+        */
+       public function testReset() {
+               $dbSiteStore = $this->getMockBuilder( 'SiteStore' )
+                       ->disableOriginalConstructor()
+                       ->getMock();
+
+               // php 5.3 compatibility!
+               $self = $this;
+
+               $dbSiteStore->expects( $this->any() )
+                       ->method( 'getSite' )
+                       ->will( $this->returnValue( $self->getTestSite() ) );
+
+               $dbSiteStore->expects( $this->any() )
+                       ->method( 'getSites' )
+                       ->will( $this->returnCallback( function() use( $self ) {
+                               $siteList = new SiteList();
+                               $siteList->setSite( $self->getTestSite() );
+
+                               return $siteList;
+                       } ) );
+
+               $store = new CachingSiteStore( $dbSiteStore, wfGetMainCache() );
+
+               // initialize internal cache
+               $this->assertGreaterThan( 0, $store->getSites()->count(), 'count sites' );
+
+               $store->getSite( 'enwiki' )->setLanguageCode( 'en-ca' );
+
+               // sanity check: $store should have the new language code for 'enwiki'
+               $this->assertEquals( 'en-ca', $store->getSite( 'enwiki' )->getLanguageCode(), 'sanity check' );
+
+               // purge cache
+               $store->reset();
+
+               // the internal cache of $store should be updated, and now pulling
+               // the site from the 'fallback' DBSiteStore with the original language code.
+               $this->assertEquals( 'en', $store->getSite( 'enwiki' )->getLanguageCode(), 'reset' );
+       }
+
+       public function getTestSite() {
+               $enwiki = new MediaWikiSite();
+               $enwiki->setGlobalId( 'enwiki' );
+               $enwiki->setLanguageCode( 'en' );
+
+               return $enwiki;
+       }
+
+       /**
+        * @covers CachingSiteStore::clear
+        */
+       public function testClear() {
+               $store = new CachingSiteStore( new HashSiteStore(), wfGetMainCache() );
+               $this->assertTrue( $store->clear() );
+
+               $site = $store->getSite( 'enwiki' );
+               $this->assertNull( $site );
+
+               $sites = $store->getSites();
+               $this->assertEquals( 0, $sites->count() );
+       }
+
+       private function getHashSiteStore( array $sites ) {
+               $siteStore = new HashSiteStore();
+               $siteStore->saveSites( $sites );
+
+               return $siteStore;
+       }
+
+}
diff --git a/tests/phpunit/includes/site/DBSiteStoreTest.php b/tests/phpunit/includes/site/DBSiteStoreTest.php
new file mode 100644 (file)
index 0000000..09ee899
--- /dev/null
@@ -0,0 +1,134 @@
+<?php
+
+/**
+ * Tests for the DBSiteStore class.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @since 1.21
+ *
+ * @ingroup Site
+ * @ingroup Test
+ *
+ * @group Site
+ * @group Database
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+class DBSiteStoreTest extends MediaWikiTestCase {
+
+       /**
+        * @covers DBSiteStore::getSites
+        */
+       public function testGetSites() {
+               $expectedSites = TestSites::getSites();
+               TestSites::insertIntoDb();
+
+               $store = new DBSiteStore();
+
+               $sites = $store->getSites();
+
+               $this->assertInstanceOf( 'SiteList', $sites );
+
+               /**
+                * @var Site $site
+                */
+               foreach ( $sites as $site ) {
+                       $this->assertInstanceOf( 'Site', $site );
+               }
+
+               foreach ( $expectedSites as $site ) {
+                       if ( $site->getGlobalId() !== null ) {
+                               $this->assertTrue( $sites->hasSite( $site->getGlobalId() ) );
+                       }
+               }
+       }
+
+       /**
+        * @covers DBSiteStore::saveSites
+        */
+       public function testSaveSites() {
+               $store = new DBSiteStore();
+
+               $sites = array();
+
+               $site = new Site();
+               $site->setGlobalId( 'ertrywuutr' );
+               $site->setLanguageCode( 'en' );
+               $sites[] = $site;
+
+               $site = new MediaWikiSite();
+               $site->setGlobalId( 'sdfhxujgkfpth' );
+               $site->setLanguageCode( 'nl' );
+               $sites[] = $site;
+
+               $this->assertTrue( $store->saveSites( $sites ) );
+
+               $site = $store->getSite( 'ertrywuutr' );
+               $this->assertInstanceOf( 'Site', $site );
+               $this->assertEquals( 'en', $site->getLanguageCode() );
+               $this->assertTrue( is_integer( $site->getInternalId() ) );
+               $this->assertTrue( $site->getInternalId() >= 0 );
+
+               $site = $store->getSite( 'sdfhxujgkfpth' );
+               $this->assertInstanceOf( 'Site', $site );
+               $this->assertEquals( 'nl', $site->getLanguageCode() );
+               $this->assertTrue( is_integer( $site->getInternalId() ) );
+               $this->assertTrue( $site->getInternalId() >= 0 );
+       }
+
+       /**
+        * @covers DBSiteStore::reset
+        */
+       public function testReset() {
+               $store1 = new DBSiteStore();
+               $store2 = new DBSiteStore();
+
+               // initialize internal cache
+               $this->assertGreaterThan( 0, $store1->getSites()->count() );
+               $this->assertGreaterThan( 0, $store2->getSites()->count() );
+
+               // Clear actual data. Will purge the external cache and reset the internal
+               // cache in $store1, but not the internal cache in store2.
+               $this->assertTrue( $store1->clear() );
+
+               // sanity check: $store2 should have a stale cache now
+               $this->assertNotNull( $store2->getSite( 'enwiki' ) );
+
+               // purge cache
+               $store2->reset();
+
+               // ...now the internal cache of $store2 should be updated and thus empty.
+               $site = $store2->getSite( 'enwiki' );
+               $this->assertNull( $site );
+       }
+
+       /**
+        * @covers DBSiteStore::clear
+        */
+       public function testClear() {
+               $store = new DBSiteStore();
+               $this->assertTrue( $store->clear() );
+
+               $site = $store->getSite( 'enwiki' );
+               $this->assertNull( $site );
+
+               $sites = $store->getSites();
+               $this->assertEquals( 0, $sites->count() );
+       }
+}
diff --git a/tests/phpunit/includes/site/FileBasedSiteLookupTest.php b/tests/phpunit/includes/site/FileBasedSiteLookupTest.php
new file mode 100644 (file)
index 0000000..8103f61
--- /dev/null
@@ -0,0 +1,102 @@
+<?php
+
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @since 1.25
+ *
+ * @ingroup Site
+ * @ingroup Test
+ *
+ * @covers FileBasedSiteLookup
+ * @group Site
+ *
+ * @licence GNU GPL v2+
+ * @author Katie Filbert < aude.wiki@gmail.com >
+ */
+class FileBasedSiteLookupTest extends PHPUnit_Framework_TestCase {
+
+       protected function setUp() {
+               $this->cacheFile = $this->getCacheFile();
+       }
+
+       protected function tearDown() {
+               unlink( $this->cacheFile );
+       }
+
+       public function testGetSites() {
+               $sites = $this->getSites();
+               $cacheBuilder = $this->newSitesCacheFileBuilder( $sites );
+               $cacheBuilder->build();
+
+               $cache = new FileBasedSiteLookup( $this->cacheFile );
+               $this->assertEquals( $sites, $cache->getSites() );
+       }
+
+       public function testGetSite() {
+               $sites = $this->getSites();
+               $cacheBuilder = $this->newSitesCacheFileBuilder( $sites );
+               $cacheBuilder->build();
+
+               $cache = new FileBasedSiteLookup( $this->cacheFile );
+
+               $this->assertEquals( $sites->getSite( 'enwiktionary' ), $cache->getSite( 'enwiktionary' ) );
+       }
+
+       private function newSitesCacheFileBuilder( SiteList $sites ) {
+               return new SitesCacheFileBuilder(
+                       $this->getSiteLookup( $sites ),
+                       $this->cacheFile
+               );
+       }
+
+       private function getSiteLookup( SiteList $sites ) {
+               $siteLookup = $this->getMockBuilder( 'SiteLookup' )
+                       ->disableOriginalConstructor()
+                       ->getMock();
+
+               $siteLookup->expects( $this->any() )
+                       ->method( 'getSites' )
+                       ->will( $this->returnValue( $sites ) );
+
+               return $siteLookup;
+       }
+
+       private function getSites() {
+               $sites = array();
+
+               $site = new Site();
+               $site->setGlobalId( 'foobar' );
+               $sites[] = $site;
+
+               $site = new MediaWikiSite();
+               $site->setGlobalId( 'enwiktionary' );
+               $site->setGroup( 'wiktionary' );
+               $site->setLanguageCode( 'en' );
+               $site->addNavigationId( 'enwiktionary' );
+               $site->setPath( MediaWikiSite::PATH_PAGE, "https://en.wiktionary.org/wiki/$1" );
+               $site->setPath( MediaWikiSite::PATH_FILE, "https://en.wiktionary.org/w/$1" );
+               $sites[] = $site;
+
+               return new SiteList( $sites );
+       }
+
+       private function getCacheFile() {
+               return tempnam( sys_get_temp_dir(), 'mw-test-sitelist' );
+       }
+
+}
diff --git a/tests/phpunit/includes/site/SiteListFileCacheBuilderTest.php b/tests/phpunit/includes/site/SiteListFileCacheBuilderTest.php
deleted file mode 100644 (file)
index bbe8cc7..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-<?php
-
-/**
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @since 1.25
- *
- * @ingroup Site
- * @ingroup Test
- *
- * @covers SiteListFileCacheBuilder
- * @group Site
- *
- * @licence GNU GPL v2+
- * @author Katie Filbert < aude.wiki@gmail.com >
- */
-class SiteListFileCacheBuilderTest extends PHPUnit_Framework_TestCase {
-
-       protected function setUp() {
-               $this->cacheFile = $this->getCacheFile();
-       }
-
-       protected function tearDown() {
-               unlink( $this->cacheFile );
-       }
-
-       public function testBuild() {
-               $cacheBuilder = $this->newSiteListFileCacheBuilder( $this->getSites() );
-               $cacheBuilder->build();
-
-               $contents = file_get_contents( $this->cacheFile );
-               $this->assertEquals( json_encode( $this->getExpectedData() ), $contents );
-       }
-
-       private function getExpectedData() {
-               return array(
-                       'sites' => array(
-                               'foobar' => array(
-                                       'globalid' => 'foobar',
-                                       'type' => 'unknown',
-                                       'group' => 'none',
-                                       'source' => 'local',
-                                       'language' => null,
-                                       'localids' => array(),
-                                       'config' => array(),
-                                       'data' => array(),
-                                       'forward' => false,
-                                       'internalid' => null,
-                                       'identifiers' => array()
-                               ),
-                               'enwiktionary' => array(
-                                       'globalid' => 'enwiktionary',
-                                       'type' => 'mediawiki',
-                                       'group' => 'wiktionary',
-                                       'source' => 'local',
-                                       'language' => 'en',
-                                       'localids' => array(
-                                               'equivalent' => array( 'enwiktionary' )
-                                       ),
-                                       'config' => array(),
-                                       'data' => array(
-                                               'paths' => array(
-                                                       'page_path' => 'https://en.wiktionary.org/wiki/$1',
-                                                       'file_path' => 'https://en.wiktionary.org/w/$1'
-                                               )
-                                       ),
-                                       'forward' => false,
-                                       'internalid' => null,
-                                       'identifiers' => array(
-                                               array(
-                                                       'type' => 'equivalent',
-                                                       'key' => 'enwiktionary'
-                                               )
-                                       )
-                               )
-                       )
-               );
-       }
-
-       private function newSiteListFileCacheBuilder( SiteList $sites ) {
-               return new SiteListFileCacheBuilder(
-                       $this->getSiteSQLStore( $sites ),
-                       $this->cacheFile
-               );
-       }
-
-       private function getSiteSQLStore( SiteList $sites ) {
-               $siteSQLStore = $this->getMockBuilder( 'SiteSQLStore' )
-                       ->disableOriginalConstructor()
-                       ->getMock();
-
-               $siteSQLStore->expects( $this->any() )
-                       ->method( 'getSites' )
-                       ->will( $this->returnValue( $sites ) );
-
-               return $siteSQLStore;
-       }
-
-       private function getSites() {
-               $sites = array();
-
-               $site = new Site();
-               $site->setGlobalId( 'foobar' );
-               $sites[] = $site;
-
-               $site = new MediaWikiSite();
-               $site->setGlobalId( 'enwiktionary' );
-               $site->setGroup( 'wiktionary' );
-               $site->setLanguageCode( 'en' );
-               $site->addNavigationId( 'enwiktionary' );
-               $site->setPath( MediaWikiSite::PATH_PAGE, "https://en.wiktionary.org/wiki/$1" );
-               $site->setPath( MediaWikiSite::PATH_FILE, "https://en.wiktionary.org/w/$1" );
-               $sites[] = $site;
-
-               return new SiteList( $sites );
-       }
-
-       private function getCacheFile() {
-               return tempnam( sys_get_temp_dir(), 'mw-test-sitelist' );
-       }
-
-}
diff --git a/tests/phpunit/includes/site/SiteListFileCacheTest.php b/tests/phpunit/includes/site/SiteListFileCacheTest.php
deleted file mode 100644 (file)
index 05dcd8a..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-<?php
-
-/**
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @since 1.25
- *
- * @ingroup Site
- * @ingroup Test
- *
- * @covers SiteListFileCache
- * @group Site
- *
- * @licence GNU GPL v2+
- * @author Katie Filbert < aude.wiki@gmail.com >
- */
-class SiteListFileCacheTest extends PHPUnit_Framework_TestCase {
-
-       protected function setUp() {
-               $this->cacheFile = $this->getCacheFile();
-       }
-
-       protected function tearDown() {
-               unlink( $this->cacheFile );
-       }
-
-       public function testGetSites() {
-               $sites = $this->getSites();
-               $cacheBuilder = $this->newSiteListFileCacheBuilder( $sites );
-               $cacheBuilder->build();
-
-               $cache = new SiteListFileCache( $this->cacheFile );
-               $this->assertEquals( $sites, $cache->getSites() );
-       }
-
-       public function testGetSite() {
-               $sites = $this->getSites();
-               $cacheBuilder = $this->newSiteListFileCacheBuilder( $sites );
-               $cacheBuilder->build();
-
-               $cache = new SiteListFileCache( $this->cacheFile );
-
-               $this->assertEquals( $sites->getSite( 'enwiktionary' ), $cache->getSite( 'enwiktionary' ) );
-       }
-
-       private function newSiteListFileCacheBuilder( SiteList $sites ) {
-               return new SiteListFileCacheBuilder(
-                       $this->getSiteSQLStore( $sites ),
-                       $this->cacheFile
-               );
-       }
-
-       private function getSiteSQLStore( SiteList $sites ) {
-               $siteSQLStore = $this->getMockBuilder( 'SiteSQLStore' )
-                       ->disableOriginalConstructor()
-                       ->getMock();
-
-               $siteSQLStore->expects( $this->any() )
-                       ->method( 'getSites' )
-                       ->will( $this->returnValue( $sites ) );
-
-               return $siteSQLStore;
-       }
-
-       private function getSites() {
-               $sites = array();
-
-               $site = new Site();
-               $site->setGlobalId( 'foobar' );
-               $sites[] = $site;
-
-               $site = new MediaWikiSite();
-               $site->setGlobalId( 'enwiktionary' );
-               $site->setGroup( 'wiktionary' );
-               $site->setLanguageCode( 'en' );
-               $site->addNavigationId( 'enwiktionary' );
-               $site->setPath( MediaWikiSite::PATH_PAGE, "https://en.wiktionary.org/wiki/$1" );
-               $site->setPath( MediaWikiSite::PATH_FILE, "https://en.wiktionary.org/w/$1" );
-               $sites[] = $site;
-
-               return new SiteList( $sites );
-       }
-
-       private function getCacheFile() {
-               return tempnam( sys_get_temp_dir(), 'mw-test-sitelist' );
-       }
-
-}
index 6002c1a..f466e10 100644 (file)
@@ -1,8 +1,6 @@
 <?php
 
 /**
- * Tests for the SiteSQLStore class.
- *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -19,7 +17,7 @@
  * http://www.gnu.org/copyleft/gpl.html
  *
  * @file
- * @since 1.21
+ * @since 1.25
  *
  * @ingroup Site
  * @ingroup Test
  * @group Database
  *
  * @licence GNU GPL v2+
- * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ * @author Katie Filbert < aude.wiki@gmail.com >
  */
 class SiteSQLStoreTest extends MediaWikiTestCase {
 
        /**
-        * @covers SiteSQLStore::getSites
-        */
-       public function testGetSites() {
-               $expectedSites = TestSites::getSites();
-               TestSites::insertIntoDb();
-
-               $store = SiteSQLStore::newInstance();
-
-               $sites = $store->getSites();
-
-               $this->assertInstanceOf( 'SiteList', $sites );
-
-               /**
-                * @var Site $site
-                */
-               foreach ( $sites as $site ) {
-                       $this->assertInstanceOf( 'Site', $site );
-               }
-
-               foreach ( $expectedSites as $site ) {
-                       if ( $site->getGlobalId() !== null ) {
-                               $this->assertTrue( $sites->hasSite( $site->getGlobalId() ) );
-                       }
-               }
-       }
-
-       /**
-        * @covers SiteSQLStore::saveSites
-        */
-       public function testSaveSites() {
-               $store = SiteSQLStore::newInstance();
-
-               $sites = array();
-
-               $site = new Site();
-               $site->setGlobalId( 'ertrywuutr' );
-               $site->setLanguageCode( 'en' );
-               $sites[] = $site;
-
-               $site = new MediaWikiSite();
-               $site->setGlobalId( 'sdfhxujgkfpth' );
-               $site->setLanguageCode( 'nl' );
-               $sites[] = $site;
-
-               $this->assertTrue( $store->saveSites( $sites ) );
-
-               $site = $store->getSite( 'ertrywuutr' );
-               $this->assertInstanceOf( 'Site', $site );
-               $this->assertEquals( 'en', $site->getLanguageCode() );
-               $this->assertTrue( is_integer( $site->getInternalId() ) );
-               $this->assertTrue( $site->getInternalId() >= 0 );
-
-               $site = $store->getSite( 'sdfhxujgkfpth' );
-               $this->assertInstanceOf( 'Site', $site );
-               $this->assertEquals( 'nl', $site->getLanguageCode() );
-               $this->assertTrue( is_integer( $site->getInternalId() ) );
-               $this->assertTrue( $site->getInternalId() >= 0 );
-       }
-
-       /**
-        * @covers SiteSQLStore::reset
+        * @covers SiteSQLStore::newInstance
         */
-       public function testReset() {
-               $store1 = SiteSQLStore::newInstance();
-               $store2 = SiteSQLStore::newInstance();
-
-               // initialize internal cache
-               $this->assertGreaterThan( 0, $store1->getSites()->count() );
-               $this->assertGreaterThan( 0, $store2->getSites()->count() );
-
-               // Clear actual data. Will purge the external cache and reset the internal
-               // cache in $store1, but not the internal cache in store2.
-               $this->assertTrue( $store1->clear() );
-
-               // sanity check: $store2 should have a stale cache now
-               $this->assertNotNull( $store2->getSite( 'enwiki' ) );
-
-               // purge cache
-               $store2->reset();
-
-               // ...now the internal cache of $store2 should be updated and thus empty.
-               $site = $store2->getSite( 'enwiki' );
-               $this->assertNull( $site );
+       public function testNewInstance() {
+               $siteStore = SiteSQLStore::newInstance();
+               $this->assertInstanceOf( 'SiteSQLStore', $siteStore );
        }
 
-       /**
-        * @covers SiteSQLStore::clear
-        */
-       public function testClear() {
-               $store = SiteSQLStore::newInstance();
-               $this->assertTrue( $store->clear() );
-
-               $site = $store->getSite( 'enwiki' );
-               $this->assertNull( $site );
-
-               $sites = $store->getSites();
-               $this->assertEquals( 0, $sites->count() );
-       }
 }
diff --git a/tests/phpunit/includes/site/SitesCacheFileBuilderTest.php b/tests/phpunit/includes/site/SitesCacheFileBuilderTest.php
new file mode 100644 (file)
index 0000000..8299423
--- /dev/null
@@ -0,0 +1,136 @@
+<?php
+
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @since 1.25
+ *
+ * @ingroup Site
+ * @ingroup Test
+ *
+ * @covers SitesCacheFileBuilder
+ * @group Site
+ *
+ * @licence GNU GPL v2+
+ * @author Katie Filbert < aude.wiki@gmail.com >
+ */
+class SitesCacheFileBuilderTest extends PHPUnit_Framework_TestCase {
+
+       protected function setUp() {
+               $this->cacheFile = $this->getCacheFile();
+       }
+
+       protected function tearDown() {
+               unlink( $this->cacheFile );
+       }
+
+       public function testBuild() {
+               $cacheBuilder = $this->newSitesCacheFileBuilder( $this->getSites() );
+               $cacheBuilder->build();
+
+               $contents = file_get_contents( $this->cacheFile );
+               $this->assertEquals( json_encode( $this->getExpectedData() ), $contents );
+       }
+
+       private function getExpectedData() {
+               return array(
+                       'sites' => array(
+                               'foobar' => array(
+                                       'globalid' => 'foobar',
+                                       'type' => 'unknown',
+                                       'group' => 'none',
+                                       'source' => 'local',
+                                       'language' => null,
+                                       'localids' => array(),
+                                       'config' => array(),
+                                       'data' => array(),
+                                       'forward' => false,
+                                       'internalid' => null,
+                                       'identifiers' => array()
+                               ),
+                               'enwiktionary' => array(
+                                       'globalid' => 'enwiktionary',
+                                       'type' => 'mediawiki',
+                                       'group' => 'wiktionary',
+                                       'source' => 'local',
+                                       'language' => 'en',
+                                       'localids' => array(
+                                               'equivalent' => array( 'enwiktionary' )
+                                       ),
+                                       'config' => array(),
+                                       'data' => array(
+                                               'paths' => array(
+                                                       'page_path' => 'https://en.wiktionary.org/wiki/$1',
+                                                       'file_path' => 'https://en.wiktionary.org/w/$1'
+                                               )
+                                       ),
+                                       'forward' => false,
+                                       'internalid' => null,
+                                       'identifiers' => array(
+                                               array(
+                                                       'type' => 'equivalent',
+                                                       'key' => 'enwiktionary'
+                                               )
+                                       )
+                               )
+                       )
+               );
+       }
+
+       private function newSitesCacheFileBuilder( SiteList $sites ) {
+               return new SitesCacheFileBuilder(
+                       $this->getSiteLookup( $sites ),
+                       $this->cacheFile
+               );
+       }
+
+       private function getSiteLookup( SiteList $sites ) {
+               $siteLookup = $this->getMockBuilder( 'SiteLookup' )
+                       ->disableOriginalConstructor()
+                       ->getMock();
+
+               $siteLookup->expects( $this->any() )
+                       ->method( 'getSites' )
+                       ->will( $this->returnValue( $sites ) );
+
+               return $siteLookup;
+       }
+
+       private function getSites() {
+               $sites = array();
+
+               $site = new Site();
+               $site->setGlobalId( 'foobar' );
+               $sites[] = $site;
+
+               $site = new MediaWikiSite();
+               $site->setGlobalId( 'enwiktionary' );
+               $site->setGroup( 'wiktionary' );
+               $site->setLanguageCode( 'en' );
+               $site->addNavigationId( 'enwiktionary' );
+               $site->setPath( MediaWikiSite::PATH_PAGE, "https://en.wiktionary.org/wiki/$1" );
+               $site->setPath( MediaWikiSite::PATH_FILE, "https://en.wiktionary.org/w/$1" );
+               $sites[] = $site;
+
+               return new SiteList( $sites );
+       }
+
+       private function getCacheFile() {
+               return tempnam( sys_get_temp_dir(), 'mw-test-sitelist' );
+       }
+
+}
index af314ba..b3ff701 100644 (file)
@@ -108,7 +108,7 @@ class TestSites {
         * @since 0.1
         */
        public static function insertIntoDb() {
-               $sitesTable = SiteSQLStore::newInstance();
+               $sitesTable = new DBSiteStore();
                $sitesTable->clear();
                $sitesTable->saveSites( TestSites::getSites() );
        }
index 681b2d2..cde6547 100644 (file)
@@ -130,10 +130,14 @@ class AutoLoaderTest extends MediaWikiTestCase {
        }
 
        function testWrongCaseClass() {
+               $this->setMwGlobals( 'wgAutoloadAttemptLowercase', true );
+
                $this->assertTrue( class_exists( 'testautoLoadedcamlCLASS' ) );
        }
 
        function testWrongCaseSerializedClass() {
+               $this->setMwGlobals( 'wgAutoloadAttemptLowercase', true );
+
                $dummyCereal = 'O:29:"testautoloadedserializedclass":0:{}';
                $uncerealized = unserialize( $dummyCereal );
                $this->assertFalse( $uncerealized instanceof __PHP_Incomplete_Class,
index 494727a..8430413 100644 (file)
@@ -73,6 +73,7 @@ return array(
                        'tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js',
                        'tests/qunit/suites/resources/mediawiki.api/mediawiki.api.test.js',
                        'tests/qunit/suites/resources/mediawiki.api/mediawiki.api.category.test.js',
+                       'tests/qunit/suites/resources/mediawiki.api/mediawiki.api.options.test.js',
                        'tests/qunit/suites/resources/mediawiki.api/mediawiki.api.parse.test.js',
                        'tests/qunit/suites/resources/mediawiki.api/mediawiki.api.watch.test.js',
                        'tests/qunit/suites/resources/mediawiki.special/mediawiki.special.recentchanges.test.js',
@@ -99,6 +100,7 @@ return array(
                        'jquery.textSelection',
                        'mediawiki.api',
                        'mediawiki.api.category',
+                       'mediawiki.api.options',
                        'mediawiki.api.parse',
                        'mediawiki.api.watch',
                        'mediawiki.jqueryMsg',
diff --git a/tests/qunit/suites/resources/mediawiki.api/mediawiki.api.options.test.js b/tests/qunit/suites/resources/mediawiki.api/mediawiki.api.options.test.js
new file mode 100644 (file)
index 0000000..c0a6585
--- /dev/null
@@ -0,0 +1,78 @@
+( function ( mw ) {
+       QUnit.module( 'mediawiki.api.options', QUnit.newMwEnvironment( {
+               setup: function () {
+                       this.server = this.sandbox.useFakeServer();
+               }
+       } ) );
+
+       QUnit.test( 'saveOption', function ( assert ) {
+               QUnit.expect( 2 );
+
+               var
+                       api = new mw.Api(),
+                       stub = this.sandbox.stub( mw.Api.prototype, 'saveOptions' );
+
+               api.saveOption( 'foo', 'bar' );
+
+               assert.ok( stub.calledOnce, '#saveOptions called once' );
+               assert.deepEqual( stub.getCall( 0 ).args, [ { foo: 'bar' } ], '#saveOptions called correctly' );
+       } );
+
+       QUnit.test( 'saveOptions', function ( assert ) {
+               QUnit.expect( 13 );
+
+               var api = new mw.Api();
+
+               // We need to respond to the request for token first, otherwise the other requests won't be sent
+               // until after the server.respond call, which confuses sinon terribly. This sucks a lot.
+               api.getToken( 'options' );
+               this.server.respond(
+                       /action=tokens.*&type=options/,
+                       [ 200, { 'Content-Type': 'application/json' },
+                               '{ "tokens": { "optionstoken": "+\\\\" } }' ]
+               );
+
+               api.saveOptions( {} ).done( function () {
+                       assert.ok( true, 'Request completed: empty case' );
+               } );
+               api.saveOptions( { foo: 'bar' } ).done( function () {
+                       assert.ok( true, 'Request completed: simple' );
+               } );
+               api.saveOptions( { foo: 'bar', baz: 'quux' } ).done( function () {
+                       assert.ok( true, 'Request completed: two options' );
+               } );
+               api.saveOptions( { foo: 'bar|quux', bar: 'a|b|c', baz: 'quux' } ).done( function () {
+                       assert.ok( true, 'Request completed: not bundleable' );
+               } );
+               api.saveOptions( { foo: null } ).done( function () {
+                       assert.ok( true, 'Request completed: reset an option' );
+               } );
+               api.saveOptions( { 'foo|bar=quux': null } ).done( function () {
+                       assert.ok( true, 'Request completed: reset an option, not bundleable' );
+               } );
+
+               // Requests are POST, match requestBody instead of url
+               this.server.respond( function ( request ) {
+                       switch ( request.requestBody ) {
+                               // simple
+                               case 'action=options&format=json&change=foo%3Dbar&token=%2B%5C':
+                               // two options
+                               case 'action=options&format=json&change=foo%3Dbar%7Cbaz%3Dquux&token=%2B%5C':
+                               // not bundleable
+                               case 'action=options&format=json&optionname=foo&optionvalue=bar%7Cquux&token=%2B%5C':
+                               case 'action=options&format=json&optionname=bar&optionvalue=a%7Cb%7Cc&token=%2B%5C':
+                               case 'action=options&format=json&change=baz%3Dquux&token=%2B%5C':
+                               // reset an option
+                               case 'action=options&format=json&change=foo&token=%2B%5C':
+                               // reset an option, not bundleable
+                               case 'action=options&format=json&optionname=foo%7Cbar%3Dquux&token=%2B%5C':
+                                       assert.ok( true, 'Repond to ' + request.requestBody );
+                                       request.respond( 200, { 'Content-Type': 'application/json' },
+                                               '{ "options": "success" }' );
+                                       break;
+                               default:
+                                       assert.ok( false, 'Unexpected request:' + request.requestBody );
+                       }
+               } );
+       } );
+}( mediaWiki ) );
index 3f19a64..0b42af4 100644 (file)
@@ -1,4 +1,79 @@
 ( function ( mw, $ ) {
+       var
+               // Based on IPTest.php > testisIPv4
+               IPV4_CASES = [
+                       [false, false, 'Boolean false is not an IP'],
+                       [false, true, 'Boolean true is not an IP'],
+                       [false, '', 'Empty string is not an IP'],
+                       [false, 'abc', '"abc" is not an IP'],
+                       [false, ':', 'Colon is not an IP'],
+                       [false, '124.24.52', 'IPv4 not enough quads'],
+                       [false, '24.324.52.13', 'IPv4 out of range'],
+                       [false, '.24.52.13', 'IPv4 starts with period'],
+
+                       [true, '124.24.52.13', '124.24.52.134 is a valid IP'],
+                       [true, '1.24.52.13', '1.24.52.13 is a valid IP'],
+                       [false, '74.24.52.13/20', 'IPv4 ranges are not recognized as valid IPs']
+               ],
+
+               // Based on IPTest.php > testisIPv6
+               IPV6_CASES = [
+                       [false, ':fc:100::', 'IPv6 starting with lone ":"'],
+                       [false, 'fc:100:::', 'IPv6 ending with a ":::"'],
+                       [false, 'fc:300', 'IPv6 with only 2 words'],
+                       [false, 'fc:100:300', 'IPv6 with only 3 words'],
+
+                       [false, 'fc:100:a:d:1:e:ac:0::', 'IPv6 with 8 words ending with "::"'],
+                       [false, 'fc:100:a:d:1:e:ac:0:1::', 'IPv6 with 9 words ending with "::"'],
+
+                       [false, ':::'],
+                       [false, '::0:', 'IPv6 ending in a lone ":"'],
+
+                       [true,  '::', 'IPv6 zero address'],
+
+                       [false, '::fc:100:a:d:1:e:ac:0', 'IPv6 with "::" and 8 words'],
+                       [false, '::fc:100:a:d:1:e:ac:0:1', 'IPv6 with 9 words'],
+
+                       [false, ':fc::100', 'IPv6 starting with lone ":"'],
+                       [false, 'fc::100:', 'IPv6 ending with lone ":"'],
+                       [false, 'fc:::100', 'IPv6 with ":::" in the middle'],
+
+                       [true,  'fc::100', 'IPv6 with "::" and 2 words'],
+                       [true,  'fc::100:a', 'IPv6 with "::" and 3 words'],
+                       [true,  'fc::100:a:d', 'IPv6 with "::" and 4 words'],
+                       [true,  'fc::100:a:d:1', 'IPv6 with "::" and 5 words'],
+                       [true,  'fc::100:a:d:1:e', 'IPv6 with "::" and 6 words'],
+                       [true,  'fc::100:a:d:1:e:ac', 'IPv6 with "::" and 7 words'],
+                       [true,  '2001::df', 'IPv6 with "::" and 2 words'],
+                       [true,  '2001:5c0:1400:a::df', 'IPv6 with "::" and 5 words'],
+                       [true,  '2001:5c0:1400:a::df:2', 'IPv6 with "::" and 6 words'],
+
+                       [false, 'fc::100:a:d:1:e:ac:0', 'IPv6 with "::" and 8 words'],
+                       [false, 'fc::100:a:d:1:e:ac:0:1', 'IPv6 with 9 words']
+               ];
+
+       Array.prototype.push.apply( IPV6_CASES,
+               $.map( [
+                       'fc:100::',
+                       'fc:100:a::',
+                       'fc:100:a:d::',
+                       'fc:100:a:d:1::',
+                       'fc:100:a:d:1:e::',
+                       'fc:100:a:d:1:e:ac::',
+                       '::0',
+                       '::fc',
+                       '::fc:100',
+                       '::fc:100:a',
+                       '::fc:100:a:d',
+                       '::fc:100:a:d:1',
+                       '::fc:100:a:d:1:e',
+                       '::fc:100:a:d:1:e:ac',
+                       'fc:100:a:d:1:e:ac:0'
+               ], function ( el ) {
+                       return [[ true, el, el + ' is a valid IP' ]];
+               } )
+       );
+
        QUnit.module( 'mediawiki.util', QUnit.newMwEnvironment( {
                setup: function () {
                        $.fn.updateTooltipAccessKeys.setTestMode( true );
        } );
 
        QUnit.test( 'isIPv6Address', 40, function ( assert ) {
-               // Shortcuts
-               function assertFalseIPv6( addy, summary ) {
-                       return assert.strictEqual( mw.util.isIPv6Address( addy ), false, summary );
-               }
-
-               function assertTrueIPv6( addy, summary ) {
-                       return assert.strictEqual( mw.util.isIPv6Address( addy ), true, summary );
-               }
-
-               // Based on IPTest.php > testisIPv6
-               assertFalseIPv6( ':fc:100::', 'IPv6 starting with lone ":"' );
-               assertFalseIPv6( 'fc:100:::', 'IPv6 ending with a ":::"' );
-               assertFalseIPv6( 'fc:300', 'IPv6 with only 2 words' );
-               assertFalseIPv6( 'fc:100:300', 'IPv6 with only 3 words' );
-
-               $.each(
-                       ['fc:100::',
-                               'fc:100:a::',
-                               'fc:100:a:d::',
-                               'fc:100:a:d:1::',
-                               'fc:100:a:d:1:e::',
-                               'fc:100:a:d:1:e:ac::'], function ( i, addy ) {
-                               assertTrueIPv6( addy, addy + ' is a valid IP' );
-                       } );
-
-               assertFalseIPv6( 'fc:100:a:d:1:e:ac:0::', 'IPv6 with 8 words ending with "::"' );
-               assertFalseIPv6( 'fc:100:a:d:1:e:ac:0:1::', 'IPv6 with 9 words ending with "::"' );
-
-               assertFalseIPv6( ':::' );
-               assertFalseIPv6( '::0:', 'IPv6 ending in a lone ":"' );
-
-               assertTrueIPv6( '::', 'IPv6 zero address' );
-               $.each(
-                       ['::0',
-                               '::fc',
-                               '::fc:100',
-                               '::fc:100:a',
-                               '::fc:100:a:d',
-                               '::fc:100:a:d:1',
-                               '::fc:100:a:d:1:e',
-                               '::fc:100:a:d:1:e:ac',
-
-                               'fc:100:a:d:1:e:ac:0'], function ( i, addy ) {
-                               assertTrueIPv6( addy, addy + ' is a valid IP' );
-                       } );
-
-               assertFalseIPv6( '::fc:100:a:d:1:e:ac:0', 'IPv6 with "::" and 8 words' );
-               assertFalseIPv6( '::fc:100:a:d:1:e:ac:0:1', 'IPv6 with 9 words' );
-
-               assertFalseIPv6( ':fc::100', 'IPv6 starting with lone ":"' );
-               assertFalseIPv6( 'fc::100:', 'IPv6 ending with lone ":"' );
-               assertFalseIPv6( 'fc:::100', 'IPv6 with ":::" in the middle' );
-
-               assertTrueIPv6( 'fc::100', 'IPv6 with "::" and 2 words' );
-               assertTrueIPv6( 'fc::100:a', 'IPv6 with "::" and 3 words' );
-               assertTrueIPv6( 'fc::100:a:d', 'IPv6 with "::" and 4 words' );
-               assertTrueIPv6( 'fc::100:a:d:1', 'IPv6 with "::" and 5 words' );
-               assertTrueIPv6( 'fc::100:a:d:1:e', 'IPv6 with "::" and 6 words' );
-               assertTrueIPv6( 'fc::100:a:d:1:e:ac', 'IPv6 with "::" and 7 words' );
-               assertTrueIPv6( '2001::df', 'IPv6 with "::" and 2 words' );
-               assertTrueIPv6( '2001:5c0:1400:a::df', 'IPv6 with "::" and 5 words' );
-               assertTrueIPv6( '2001:5c0:1400:a::df:2', 'IPv6 with "::" and 6 words' );
-
-               assertFalseIPv6( 'fc::100:a:d:1:e:ac:0', 'IPv6 with "::" and 8 words' );
-               assertFalseIPv6( 'fc::100:a:d:1:e:ac:0:1', 'IPv6 with 9 words' );
+               $.each( IPV6_CASES, function ( i, ipCase ) {
+                       assert.strictEqual( mw.util.isIPv6Address( ipCase[1] ), ipCase[0], ipCase[2] );
+               } );
        } );
 
        QUnit.test( 'isIPv4Address', 11, function ( assert ) {
-               // Shortcuts
-               function assertFalseIPv4( addy, summary ) {
-                       assert.strictEqual( mw.util.isIPv4Address( addy ), false, summary );
-               }
+               $.each( IPV4_CASES, function ( i, ipCase ) {
+                       assert.strictEqual( mw.util.isIPv4Address( ipCase[1] ), ipCase[0], ipCase[2] );
+               } );
+       } );
 
-               function assertTrueIPv4( addy, summary ) {
-                       assert.strictEqual( mw.util.isIPv4Address( addy ), true, summary );
-               }
+       QUnit.test( 'isIPAddress', 51, function ( assert ) {
+               $.each( IPV4_CASES, function ( i, ipCase ) {
+                       assert.strictEqual( mw.util.isIPv4Address( ipCase[1] ), ipCase[0], ipCase[2] );
+               } );
 
-               // Based on IPTest.php > testisIPv4
-               assertFalseIPv4( false, 'Boolean false is not an IP' );
-               assertFalseIPv4( true, 'Boolean true is not an IP' );
-               assertFalseIPv4( '', 'Empty string is not an IP' );
-               assertFalseIPv4( 'abc', '"abc" is not an IP' );
-               assertFalseIPv4( ':', 'Colon is not an IP' );
-               assertFalseIPv4( '124.24.52', 'IPv4 not enough quads' );
-               assertFalseIPv4( '24.324.52.13', 'IPv4 out of range' );
-               assertFalseIPv4( '.24.52.13', 'IPv4 starts with period' );
-
-               assertTrueIPv4( '124.24.52.13', '124.24.52.134 is a valid IP' );
-               assertTrueIPv4( '1.24.52.13', '1.24.52.13 is a valid IP' );
-               assertFalseIPv4( '74.24.52.13/20', 'IPv4 ranges are not recogzized as valid IPs' );
+               $.each( IPV6_CASES, function ( i, ipCase ) {
+                       assert.strictEqual( mw.util.isIPv6Address( ipCase[1] ), ipCase[0], ipCase[2] );
+               } );
        } );
 }( mediaWiki, jQuery ) );